dpickett-thinking-sphinx 1.1.4

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.
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