dpickett-thinking-sphinx 1.1.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. data/LICENCE +20 -0
  2. data/README +107 -0
  3. data/lib/thinking_sphinx/active_record/delta.rb +74 -0
  4. data/lib/thinking_sphinx/active_record/has_many_association.rb +29 -0
  5. data/lib/thinking_sphinx/active_record/search.rb +57 -0
  6. data/lib/thinking_sphinx/active_record.rb +245 -0
  7. data/lib/thinking_sphinx/adapters/abstract_adapter.rb +34 -0
  8. data/lib/thinking_sphinx/adapters/mysql_adapter.rb +53 -0
  9. data/lib/thinking_sphinx/adapters/postgresql_adapter.rb +129 -0
  10. data/lib/thinking_sphinx/association.rb +144 -0
  11. data/lib/thinking_sphinx/attribute.rb +254 -0
  12. data/lib/thinking_sphinx/class_facet.rb +20 -0
  13. data/lib/thinking_sphinx/collection.rb +142 -0
  14. data/lib/thinking_sphinx/configuration.rb +236 -0
  15. data/lib/thinking_sphinx/core/string.rb +22 -0
  16. data/lib/thinking_sphinx/deltas/datetime_delta.rb +50 -0
  17. data/lib/thinking_sphinx/deltas/default_delta.rb +65 -0
  18. data/lib/thinking_sphinx/deltas/delayed_delta/delta_job.rb +24 -0
  19. data/lib/thinking_sphinx/deltas/delayed_delta/flag_as_deleted_job.rb +27 -0
  20. data/lib/thinking_sphinx/deltas/delayed_delta/job.rb +26 -0
  21. data/lib/thinking_sphinx/deltas/delayed_delta.rb +25 -0
  22. data/lib/thinking_sphinx/deltas.rb +22 -0
  23. data/lib/thinking_sphinx/facet.rb +58 -0
  24. data/lib/thinking_sphinx/facet_collection.rb +45 -0
  25. data/lib/thinking_sphinx/field.rb +172 -0
  26. data/lib/thinking_sphinx/index/builder.rb +233 -0
  27. data/lib/thinking_sphinx/index/faux_column.rb +110 -0
  28. data/lib/thinking_sphinx/index.rb +432 -0
  29. data/lib/thinking_sphinx/rails_additions.rb +133 -0
  30. data/lib/thinking_sphinx/search.rb +654 -0
  31. data/lib/thinking_sphinx/tasks.rb +128 -0
  32. data/lib/thinking_sphinx.rb +145 -0
  33. data/spec/unit/thinking_sphinx/active_record/delta_spec.rb +136 -0
  34. data/spec/unit/thinking_sphinx/active_record/has_many_association_spec.rb +53 -0
  35. data/spec/unit/thinking_sphinx/active_record/search_spec.rb +107 -0
  36. data/spec/unit/thinking_sphinx/active_record_spec.rb +256 -0
  37. data/spec/unit/thinking_sphinx/association_spec.rb +247 -0
  38. data/spec/unit/thinking_sphinx/attribute_spec.rb +212 -0
  39. data/spec/unit/thinking_sphinx/collection_spec.rb +14 -0
  40. data/spec/unit/thinking_sphinx/configuration_spec.rb +136 -0
  41. data/spec/unit/thinking_sphinx/core/string_spec.rb +9 -0
  42. data/spec/unit/thinking_sphinx/field_spec.rb +145 -0
  43. data/spec/unit/thinking_sphinx/index/builder_spec.rb +5 -0
  44. data/spec/unit/thinking_sphinx/index/faux_column_spec.rb +30 -0
  45. data/spec/unit/thinking_sphinx/index_spec.rb +54 -0
  46. data/spec/unit/thinking_sphinx/search_spec.rb +59 -0
  47. data/spec/unit/thinking_sphinx_spec.rb +129 -0
  48. data/tasks/distribution.rb +48 -0
  49. data/tasks/rails.rake +1 -0
  50. data/tasks/testing.rb +86 -0
  51. data/vendor/after_commit/LICENSE +20 -0
  52. data/vendor/after_commit/README +16 -0
  53. data/vendor/after_commit/Rakefile +22 -0
  54. data/vendor/after_commit/init.rb +5 -0
  55. data/vendor/after_commit/lib/after_commit/active_record.rb +91 -0
  56. data/vendor/after_commit/lib/after_commit/connection_adapters.rb +103 -0
  57. data/vendor/after_commit/lib/after_commit.rb +42 -0
  58. data/vendor/after_commit/test/after_commit_test.rb +53 -0
  59. data/vendor/delayed_job/lib/delayed/job.rb +251 -0
  60. data/vendor/delayed_job/lib/delayed/message_sending.rb +7 -0
  61. data/vendor/delayed_job/lib/delayed/performable_method.rb +55 -0
  62. data/vendor/delayed_job/lib/delayed/worker.rb +54 -0
  63. data/vendor/riddle/lib/riddle/client/filter.rb +53 -0
  64. data/vendor/riddle/lib/riddle/client/message.rb +65 -0
  65. data/vendor/riddle/lib/riddle/client/response.rb +84 -0
  66. data/vendor/riddle/lib/riddle/client.rb +619 -0
  67. data/vendor/riddle/lib/riddle/configuration/distributed_index.rb +48 -0
  68. data/vendor/riddle/lib/riddle/configuration/index.rb +142 -0
  69. data/vendor/riddle/lib/riddle/configuration/indexer.rb +19 -0
  70. data/vendor/riddle/lib/riddle/configuration/remote_index.rb +17 -0
  71. data/vendor/riddle/lib/riddle/configuration/searchd.rb +25 -0
  72. data/vendor/riddle/lib/riddle/configuration/section.rb +37 -0
  73. data/vendor/riddle/lib/riddle/configuration/source.rb +23 -0
  74. data/vendor/riddle/lib/riddle/configuration/sql_source.rb +34 -0
  75. data/vendor/riddle/lib/riddle/configuration/xml_source.rb +28 -0
  76. data/vendor/riddle/lib/riddle/configuration.rb +33 -0
  77. data/vendor/riddle/lib/riddle/controller.rb +44 -0
  78. data/vendor/riddle/lib/riddle.rb +30 -0
  79. metadata +158 -0
data/LICENCE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 Pat Allan
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,107 @@
1
+ = Thinking Sphinx
2
+
3
+ == Usage
4
+
5
+ First, if you haven't done so already, check out the main usage[http://ts.freelancing-gods.com/usage.html] page. Once you've done that, the next place to look for information is the specific method docs - ThinkingSphinx::Search and ThinkingSphinx::Index::Builder in particular.
6
+
7
+ Keep in mind that while Thinking Sphinx works for ActiveRecord with Merb, it doesn't yet support DataMapper (although that is planned).
8
+
9
+ == Contributing
10
+
11
+ Fork on GitHub and after you've committed tested patches, send a pull request.
12
+
13
+ To get the spec suite running, you will need to install the not-a-mock gem if you don't already have it:
14
+
15
+ git clone git://github.com/freelancing-god/not-a-mock.git
16
+ cd not-a-mock
17
+ rake gem
18
+ gem install pkg/not_a_mock-1.1.0.gem
19
+
20
+ Then install the ginger gem. The steps are the same, except that you might need to sudo the gem install:
21
+
22
+ git clone git://github.com/freelancing-god/ginger.git
23
+ cd ginger
24
+ rake gem
25
+ sudo gem install pkg/ginger-1.1.0.gem
26
+
27
+ Then set up your database:
28
+
29
+ cp spec/fixtures/database.yml.default spec/fixtures/database.yml
30
+ mysqladmin -u root create thinking_sphinx
31
+
32
+ Make sure you don't have another Sphinx daemon (searchd) running. If you do, quit it with "rake ts:stop"
33
+ in the app root.
34
+
35
+ You should now have a passing test suite from which to build your patch on.
36
+
37
+ rake spec
38
+
39
+ If you get the message "Failed to start searchd daemon", run the spec with sudo:
40
+
41
+ sudo rake spec
42
+
43
+ If you quit the spec suite before it's completed, you may be left with data in the test
44
+ database, causing the next run to have failures. Let that run complete and then try again.
45
+
46
+ == Contributors
47
+
48
+ Since I first released this library, there's been quite a few people who have submitted patches, to my immense gratitude. Others have suggested syntax changes and general improvements. So my thanks to the following people:
49
+
50
+ - Joost Hietbrink
51
+ - Jonathan Conway
52
+ - Gregory Mirzayantz
53
+ - Tung Nguyen
54
+ - Sean Cribbs
55
+ - Benoit Caccinolo
56
+ - John Barton
57
+ - Oliver Beddows
58
+ - Arthur Zapparoli
59
+ - Dusty Doris
60
+ - Marcus Crafter
61
+ - Patrick Lenz
62
+ - Björn Andreasson
63
+ - James Healy
64
+ - Jae-Jun Hwang
65
+ - Xavier Shay
66
+ - Jason Rust
67
+ - Gopal Patel
68
+ - Chris Heald
69
+ - Peter Vandenberk
70
+ - Josh French
71
+ - Andrew Bennett
72
+ - Jordan Fowler
73
+ - Seth Walker
74
+ - Joe Noon
75
+ - Wolfgang Postler
76
+ - Rick Olson
77
+ - Killian Murphy
78
+ - Morten Primdahl
79
+ - Ryan Bates
80
+ - David Eisinger
81
+ - Shay Arnett
82
+ - Minh Tran
83
+ - Jeremy Durham
84
+ - Piotr Sarnacki
85
+ - Matt Johnson
86
+ - Nicolas Blanco
87
+ - Max Lapshin
88
+ - Josh Natanson
89
+ - Philip Hallstrom
90
+ - Christian Rishøj
91
+ - Mike Flester
92
+ - Jim Remsik
93
+ - Kennon Ballou
94
+ - Henrik Nyh
95
+ - Emil Tin
96
+ - Doug Cole
97
+ - Ed Hickey
98
+ - Evan Weaver
99
+ - Thibaut Barrere
100
+ - Kristopher Chambers
101
+ - Dmitrij Smalko
102
+ - Aleksey Yeschenko
103
+ - Lachie Cox
104
+ - Lourens Naude
105
+ - Tom Davies
106
+ - Dan Pickett
107
+ - Alex Caudill
@@ -0,0 +1,74 @@
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
+ # Temporarily disable delta indexing inside a block, then perform a single
16
+ # rebuild of index at the end.
17
+ #
18
+ # Useful when performing updates to batches of models to prevent
19
+ # the delta index being rebuilt after each individual update.
20
+ #
21
+ # In the following example, the delta index will only be rebuilt once,
22
+ # not 10 times.
23
+ #
24
+ # SomeModel.suspended_delta do
25
+ # 10.times do
26
+ # SomeModel.create( ... )
27
+ # end
28
+ # end
29
+ #
30
+ def suspended_delta(reindex_after = true, &block)
31
+ original_setting = ThinkingSphinx.deltas_enabled?
32
+ ThinkingSphinx.deltas_enabled = false
33
+ begin
34
+ yield
35
+ ensure
36
+ ThinkingSphinx.deltas_enabled = original_setting
37
+ self.index_delta if reindex_after
38
+ end
39
+ end
40
+
41
+ # Build the delta index for the related model. This won't be called
42
+ # if running in the test environment.
43
+ #
44
+ def index_delta(instance = nil)
45
+ delta_object.index(self, instance)
46
+ end
47
+
48
+ def delta_object
49
+ self.sphinx_indexes.first.delta_object
50
+ end
51
+ end
52
+
53
+ private
54
+
55
+ # Set the delta value for the model to be true.
56
+ def toggle_delta
57
+ self.class.delta_object.toggle(self) if should_toggle_delta?
58
+ end
59
+
60
+ # Build the delta index for the related model. This won't be called
61
+ # if running in the test environment.
62
+ #
63
+ def index_delta
64
+ self.class.index_delta(self) if self.class.delta_object.toggled(self)
65
+ end
66
+
67
+ def should_toggle_delta?
68
+ !self.respond_to?(:changed?) || self.changed? || self.new_record?
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,29 @@
1
+ module ThinkingSphinx
2
+ module ActiveRecord
3
+ module HasManyAssociation
4
+ def search(*args)
5
+ foreign_key = @reflection.primary_key_name
6
+ stack = [@reflection.options[:through]].compact
7
+
8
+ attribute = nil
9
+ (@reflection.klass.sphinx_indexes || []).each do |index|
10
+ attribute = index.attributes.detect { |attrib|
11
+ attrib.columns.length == 1 &&
12
+ attrib.columns.first.__name == foreign_key.to_sym &&
13
+ attrib.columns.first.__stack == stack
14
+ }
15
+ break if attribute
16
+ end
17
+
18
+ raise "Missing Attribute for Foreign Key #{foreign_key}" unless attribute
19
+
20
+ options = args.extract_options!
21
+ options[:with] ||= {}
22
+ options[:with][attribute.unique_name] = @owner.id
23
+
24
+ args << options
25
+ @reflection.klass.search(*args)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,57 @@
1
+ module ThinkingSphinx
2
+ module ActiveRecord
3
+ # This module covers the specific model searches - but the syntax is
4
+ # exactly the same as the core Search class - so use that as your refence
5
+ # point.
6
+ #
7
+ module Search
8
+ def self.included(base)
9
+ base.class_eval do
10
+ class << self
11
+ # Searches for results that match the parameters provided. Will only
12
+ # return the ids for the matching objects. See
13
+ # ThinkingSphinx::Search#search for syntax examples.
14
+ #
15
+ def search_for_ids(*args)
16
+ options = args.extract_options!
17
+ options[:class] = self
18
+ args << options
19
+ ThinkingSphinx::Search.search_for_ids(*args)
20
+ end
21
+
22
+ # Searches for results limited to a single model. See
23
+ # ThinkingSphinx::Search#search for syntax examples.
24
+ #
25
+ def search(*args)
26
+ options = args.extract_options!
27
+ options[:class] = self
28
+ args << options
29
+ ThinkingSphinx::Search.search(*args)
30
+ end
31
+
32
+ def search_count(*args)
33
+ options = args.extract_options!
34
+ options[:class] = self
35
+ args << options
36
+ ThinkingSphinx::Search.count(*args)
37
+ end
38
+
39
+ def search_for_id(*args)
40
+ options = args.extract_options!
41
+ options[:class] = self
42
+ args << options
43
+ ThinkingSphinx::Search.search_for_id(*args)
44
+ end
45
+
46
+ def facets(*args)
47
+ options = args.extract_options!
48
+ options[:class] = self
49
+ args << options
50
+ ThinkingSphinx::Search.facets(*args)
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,245 @@
1
+ require 'thinking_sphinx/active_record/delta'
2
+ require 'thinking_sphinx/active_record/search'
3
+ require 'thinking_sphinx/active_record/has_many_association'
4
+
5
+ module ThinkingSphinx
6
+ # Core additions to ActiveRecord models - define_index for creating indexes
7
+ # for models. If you want to interrogate the index objects created for the
8
+ # model, you can use the class-level accessor :sphinx_indexes.
9
+ #
10
+ module ActiveRecord
11
+ def self.included(base)
12
+ base.class_eval do
13
+ class_inheritable_array :sphinx_indexes, :sphinx_facets
14
+ class << self
15
+ # Allows creation of indexes for Sphinx. If you don't do this, there
16
+ # isn't much point trying to search (or using this plugin at all,
17
+ # really).
18
+ #
19
+ # An example or two:
20
+ #
21
+ # define_index
22
+ # indexes :id, :as => :model_id
23
+ # indexes name
24
+ # end
25
+ #
26
+ # You can also grab fields from associations - multiple levels deep
27
+ # if necessary.
28
+ #
29
+ # define_index do
30
+ # indexes tags.name, :as => :tag
31
+ # indexes articles.content
32
+ # indexes orders.line_items.product.name, :as => :product
33
+ # end
34
+ #
35
+ # And it will automatically concatenate multiple fields:
36
+ #
37
+ # define_index do
38
+ # indexes [author.first_name, author.last_name], :as => :author
39
+ # end
40
+ #
41
+ # The #indexes method is for fields - if you want attributes, use
42
+ # #has instead. All the same rules apply - but keep in mind that
43
+ # attributes are for sorting, grouping and filtering, not searching.
44
+ #
45
+ # define_index do
46
+ # # fields ...
47
+ #
48
+ # has created_at, updated_at
49
+ # end
50
+ #
51
+ # One last feature is the delta index. This requires the model to
52
+ # have a boolean field named 'delta', and is enabled as follows:
53
+ #
54
+ # define_index do
55
+ # # fields ...
56
+ # # attributes ...
57
+ #
58
+ # set_property :delta => true
59
+ # end
60
+ #
61
+ # Check out the more detailed documentation for each of these methods
62
+ # at ThinkingSphinx::Index::Builder.
63
+ #
64
+ def define_index(&block)
65
+ return unless ThinkingSphinx.define_indexes?
66
+
67
+ self.sphinx_indexes ||= []
68
+ index = Index.new(self, &block)
69
+
70
+ self.sphinx_indexes << index
71
+ unless ThinkingSphinx.indexed_models.include?(self.name)
72
+ ThinkingSphinx.indexed_models << self.name
73
+ end
74
+
75
+ if index.delta?
76
+ before_save :toggle_delta
77
+ after_commit :index_delta
78
+ end
79
+
80
+ after_destroy :toggle_deleted
81
+
82
+ index
83
+ end
84
+ alias_method :sphinx_index, :define_index
85
+
86
+ def sphinx_index_options
87
+ sphinx_indexes.last.options
88
+ end
89
+
90
+ # Generate a unique CRC value for the model's name, to use to
91
+ # determine which Sphinx documents belong to which AR records.
92
+ #
93
+ # Really only written for internal use - but hey, if it's useful to
94
+ # you in some other way, awesome.
95
+ #
96
+ def to_crc32
97
+ self.name.to_crc32
98
+ end
99
+
100
+ def to_crc32s
101
+ (subclasses << self).collect { |klass| klass.to_crc32 }
102
+ end
103
+
104
+ def source_of_sphinx_index
105
+ possible_models = self.sphinx_indexes.collect { |index| index.model }
106
+ return self if possible_models.include?(self)
107
+
108
+ parent = self.superclass
109
+ while !possible_models.include?(parent) && parent != ::ActiveRecord::Base
110
+ parent = parent.superclass
111
+ end
112
+
113
+ return parent
114
+ end
115
+
116
+ def to_riddle(offset)
117
+ sphinx_database_adapter.setup
118
+
119
+ indexes = [to_riddle_for_core(offset)]
120
+ indexes << to_riddle_for_delta(offset) if sphinx_delta?
121
+ indexes << to_riddle_for_distributed
122
+ end
123
+
124
+ def sphinx_database_adapter
125
+ @sphinx_database_adapter ||=
126
+ ThinkingSphinx::AbstractAdapter.detect(self)
127
+ end
128
+
129
+ private
130
+
131
+ def sphinx_name
132
+ self.name.underscore.tr(':/\\', '_')
133
+ end
134
+
135
+ def sphinx_delta?
136
+ self.sphinx_indexes.any? { |index| index.delta? }
137
+ end
138
+
139
+ def to_riddle_for_core(offset)
140
+ index = Riddle::Configuration::Index.new("#{sphinx_name}_core")
141
+ index.path = File.join(
142
+ ThinkingSphinx::Configuration.instance.searchd_file_path, index.name
143
+ )
144
+
145
+ set_configuration_options_for_indexes index
146
+ set_field_settings_for_indexes index
147
+
148
+ self.sphinx_indexes.select { |ts_index|
149
+ ts_index.model == self
150
+ }.each_with_index do |ts_index, i|
151
+ index.sources << ts_index.to_riddle_for_core(offset, i)
152
+ end
153
+
154
+ index
155
+ end
156
+
157
+ def to_riddle_for_delta(offset)
158
+ index = Riddle::Configuration::Index.new("#{sphinx_name}_delta")
159
+ index.parent = "#{sphinx_name}_core"
160
+ index.path = File.join(ThinkingSphinx::Configuration.instance.searchd_file_path, index.name)
161
+
162
+ self.sphinx_indexes.each_with_index do |ts_index, i|
163
+ index.sources << ts_index.to_riddle_for_delta(offset, i) if ts_index.delta?
164
+ end
165
+
166
+ index
167
+ end
168
+
169
+ def to_riddle_for_distributed
170
+ index = Riddle::Configuration::DistributedIndex.new(sphinx_name)
171
+ index.local_indexes << "#{sphinx_name}_core"
172
+ index.local_indexes.unshift "#{sphinx_name}_delta" if sphinx_delta?
173
+ index
174
+ end
175
+
176
+ def set_configuration_options_for_indexes(index)
177
+ ThinkingSphinx::Configuration.instance.index_options.each do |key, value|
178
+ index.send("#{key}=".to_sym, value)
179
+ end
180
+
181
+ self.sphinx_indexes.each do |ts_index|
182
+ ts_index.options.each do |key, value|
183
+ index.send("#{key}=".to_sym, value) if ThinkingSphinx::Configuration::IndexOptions.include?(key.to_s) && !value.nil?
184
+ end
185
+ end
186
+ end
187
+
188
+ def set_field_settings_for_indexes(index)
189
+ field_names = lambda { |field| field.unique_name.to_s }
190
+
191
+ self.sphinx_indexes.each do |ts_index|
192
+ index.prefix_field_names += ts_index.prefix_fields.collect(&field_names)
193
+ index.infix_field_names += ts_index.infix_fields.collect(&field_names)
194
+ end
195
+ end
196
+ end
197
+ end
198
+
199
+ base.send(:include, ThinkingSphinx::ActiveRecord::Delta)
200
+ base.send(:include, ThinkingSphinx::ActiveRecord::Search)
201
+
202
+ ::ActiveRecord::Associations::HasManyAssociation.send(
203
+ :include, ThinkingSphinx::ActiveRecord::HasManyAssociation
204
+ )
205
+ ::ActiveRecord::Associations::HasManyThroughAssociation.send(
206
+ :include, ThinkingSphinx::ActiveRecord::HasManyAssociation
207
+ )
208
+ end
209
+
210
+ def in_core_index?
211
+ self.class.search_for_id(
212
+ self.sphinx_document_id,
213
+ "#{self.class.source_of_sphinx_index.name.underscore.tr(':/\\', '_')}_core"
214
+ )
215
+ end
216
+
217
+ def toggle_deleted
218
+ return unless ThinkingSphinx.updates_enabled? && ThinkingSphinx.sphinx_running?
219
+
220
+ config = ThinkingSphinx::Configuration.instance
221
+ client = Riddle::Client.new config.address, config.port
222
+
223
+ client.update(
224
+ "#{self.class.sphinx_indexes.first.name}_core",
225
+ ['sphinx_deleted'],
226
+ {self.sphinx_document_id => 1}
227
+ ) if self.in_core_index?
228
+
229
+ client.update(
230
+ "#{self.class.sphinx_indexes.first.name}_delta",
231
+ ['sphinx_deleted'],
232
+ {self.sphinx_document_id => 1}
233
+ ) if ThinkingSphinx.deltas_enabled? &&
234
+ self.class.sphinx_indexes.any? { |index| index.delta? } &&
235
+ self.delta
236
+ rescue ::ThinkingSphinx::ConnectionError
237
+ # nothing
238
+ end
239
+
240
+ def sphinx_document_id
241
+ (self.id * ThinkingSphinx.indexed_models.size) +
242
+ ThinkingSphinx.indexed_models.index(self.class.source_of_sphinx_index.name)
243
+ end
244
+ end
245
+ end
@@ -0,0 +1,34 @@
1
+ module ThinkingSphinx
2
+ class AbstractAdapter
3
+ def initialize(model)
4
+ @model = model
5
+ end
6
+
7
+ def setup
8
+ # Deliberately blank - subclasses should do something though. Well, if
9
+ # they need to.
10
+ end
11
+
12
+ def self.detect(model)
13
+ case model.connection.class.name
14
+ when "ActiveRecord::ConnectionAdapters::MysqlAdapter",
15
+ "ActiveRecord::ConnectionAdapters::MysqlplusAdapter"
16
+ ThinkingSphinx::MysqlAdapter.new model
17
+ when "ActiveRecord::ConnectionAdapters::PostgreSQLAdapter"
18
+ ThinkingSphinx::PostgreSQLAdapter.new model
19
+ else
20
+ raise "Invalid Database Adapter: Sphinx only supports MySQL and PostgreSQL"
21
+ end
22
+ end
23
+
24
+ def quote_with_table(column)
25
+ "#{@model.quoted_table_name}.#{@model.connection.quote_column_name(column)}"
26
+ end
27
+
28
+ protected
29
+
30
+ def connection
31
+ @connection ||= @model.connection
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,53 @@
1
+ module ThinkingSphinx
2
+ class MysqlAdapter < AbstractAdapter
3
+ def setup
4
+ # Does MySQL actually need to do anything?
5
+ end
6
+
7
+ def sphinx_identifier
8
+ "mysql"
9
+ end
10
+
11
+ def concatenate(clause, separator = ' ')
12
+ "CONCAT_WS('#{separator}', #{clause})"
13
+ end
14
+
15
+ def group_concatenate(clause, separator = ' ')
16
+ "GROUP_CONCAT(#{clause} SEPARATOR '#{separator}')"
17
+ end
18
+
19
+ def cast_to_string(clause)
20
+ "CAST(#{clause} AS CHAR)"
21
+ end
22
+
23
+ def cast_to_datetime(clause)
24
+ "UNIX_TIMESTAMP(#{clause})"
25
+ end
26
+
27
+ def cast_to_unsigned(clause)
28
+ "CAST(#{clause} AS UNSIGNED)"
29
+ end
30
+
31
+ def convert_nulls(clause, default = '')
32
+ default = "'#{default}'" if default.is_a?(String)
33
+
34
+ "IFNULL(#{clause}, #{default})"
35
+ end
36
+
37
+ def boolean(value)
38
+ value ? 1 : 0
39
+ end
40
+
41
+ def crc(clause)
42
+ "CRC32(#{clause})"
43
+ end
44
+
45
+ def utf8_query_pre
46
+ "SET NAMES utf8"
47
+ end
48
+
49
+ def time_difference(diff)
50
+ "DATE_SUB(NOW(), INTERVAL #{diff} SECOND)"
51
+ end
52
+ end
53
+ end