thinking-sphinx 1.2.13 → 1.4.0
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/README.textile +37 -4
- data/VERSION +1 -0
- data/features/abstract_inheritance.feature +10 -0
- data/features/alternate_primary_key.feature +1 -1
- data/features/attribute_updates.feature +49 -5
- data/features/deleting_instances.feature +3 -0
- data/features/excerpts.feature +8 -0
- data/features/facets.feature +15 -1
- data/features/facets_across_model.feature +2 -2
- data/features/field_sorting.feature +18 -0
- data/features/handling_edits.feature +1 -1
- data/features/searching_across_models.feature +2 -2
- data/features/searching_by_index.feature +40 -0
- data/features/searching_by_model.feature +1 -8
- data/features/sphinx_scopes.feature +33 -0
- data/features/step_definitions/alpha_steps.rb +14 -1
- data/features/step_definitions/beta_steps.rb +1 -1
- data/features/step_definitions/common_steps.rb +21 -2
- data/features/step_definitions/facet_steps.rb +4 -0
- data/features/step_definitions/scope_steps.rb +8 -0
- data/features/step_definitions/search_steps.rb +5 -0
- data/features/step_definitions/sphinx_steps.rb +8 -4
- data/features/sti_searching.feature +5 -0
- data/features/support/env.rb +7 -6
- data/features/{support → thinking_sphinx}/db/fixtures/betas.rb +1 -0
- data/features/{support → thinking_sphinx}/db/fixtures/comments.rb +1 -1
- data/features/{support → thinking_sphinx}/db/fixtures/developers.rb +2 -0
- data/features/thinking_sphinx/db/fixtures/foxes.rb +3 -0
- data/features/thinking_sphinx/db/fixtures/music.rb +4 -0
- data/features/{support → thinking_sphinx}/db/fixtures/people.rb +1 -1
- data/features/{support → thinking_sphinx}/db/fixtures/tags.rb +1 -1
- data/features/{support → thinking_sphinx}/db/migrations/create_alphas.rb +1 -0
- data/features/{support → thinking_sphinx}/db/migrations/create_developers.rb +0 -2
- data/features/thinking_sphinx/db/migrations/create_genres.rb +3 -0
- data/features/thinking_sphinx/db/migrations/create_music.rb +6 -0
- data/features/thinking_sphinx/models/alpha.rb +23 -0
- data/features/thinking_sphinx/models/andrew.rb +17 -0
- data/features/{support → thinking_sphinx}/models/beta.rb +1 -1
- data/features/{support → thinking_sphinx}/models/developer.rb +2 -2
- data/features/{support → thinking_sphinx}/models/extensible_beta.rb +1 -1
- data/features/thinking_sphinx/models/fox.rb +5 -0
- data/features/thinking_sphinx/models/genre.rb +3 -0
- data/features/thinking_sphinx/models/medium.rb +5 -0
- data/features/thinking_sphinx/models/music.rb +8 -0
- data/features/{support → thinking_sphinx}/models/person.rb +2 -1
- data/features/{support → thinking_sphinx}/models/post.rb +2 -1
- data/lib/cucumber/thinking_sphinx/external_world.rb +12 -0
- data/lib/cucumber/thinking_sphinx/internal_world.rb +13 -11
- data/lib/thinking_sphinx/active_record/attribute_updates.rb +17 -15
- data/lib/thinking_sphinx/active_record/delta.rb +0 -26
- data/lib/thinking_sphinx/active_record/has_many_association.rb +34 -11
- data/lib/thinking_sphinx/active_record/scopes.rb +46 -3
- data/lib/thinking_sphinx/active_record.rb +271 -193
- data/lib/thinking_sphinx/adapters/abstract_adapter.rb +45 -9
- data/lib/thinking_sphinx/adapters/mysql_adapter.rb +5 -1
- data/lib/thinking_sphinx/adapters/postgresql_adapter.rb +9 -1
- data/lib/thinking_sphinx/attribute.rb +67 -23
- data/lib/thinking_sphinx/auto_version.rb +24 -0
- data/lib/thinking_sphinx/bundled_search.rb +44 -0
- data/lib/thinking_sphinx/class_facet.rb +3 -2
- data/lib/thinking_sphinx/configuration.rb +78 -64
- data/lib/thinking_sphinx/context.rb +76 -0
- data/lib/thinking_sphinx/deltas/default_delta.rb +14 -20
- data/lib/thinking_sphinx/deltas.rb +0 -2
- data/lib/thinking_sphinx/deploy/capistrano.rb +1 -1
- data/lib/thinking_sphinx/excerpter.rb +1 -1
- data/lib/thinking_sphinx/facet.rb +6 -5
- data/lib/thinking_sphinx/facet_search.rb +54 -24
- data/lib/thinking_sphinx/field.rb +2 -4
- data/lib/thinking_sphinx/index/builder.rb +36 -20
- data/lib/thinking_sphinx/index/faux_column.rb +8 -0
- data/lib/thinking_sphinx/index.rb +77 -19
- data/lib/thinking_sphinx/join.rb +37 -0
- data/lib/thinking_sphinx/property.rb +9 -2
- data/lib/thinking_sphinx/rails_additions.rb +4 -4
- data/lib/thinking_sphinx/search.rb +212 -66
- data/lib/thinking_sphinx/search_methods.rb +22 -4
- data/lib/thinking_sphinx/source/internal_properties.rb +2 -2
- data/lib/thinking_sphinx/source/sql.rb +5 -3
- data/lib/thinking_sphinx/source.rb +21 -12
- data/lib/thinking_sphinx/tasks.rb +26 -58
- data/lib/thinking_sphinx/test.rb +55 -0
- data/lib/thinking_sphinx.rb +70 -38
- data/rails/init.rb +4 -2
- data/spec/{lib/thinking_sphinx → thinking_sphinx}/active_record/delta_spec.rb +6 -8
- data/spec/{lib/thinking_sphinx → thinking_sphinx}/active_record/has_many_association_spec.rb +26 -3
- data/spec/thinking_sphinx/active_record/scopes_spec.rb +176 -0
- data/spec/thinking_sphinx/active_record_spec.rb +618 -0
- data/spec/thinking_sphinx/adapters/abstract_adapter_spec.rb +134 -0
- data/spec/{lib/thinking_sphinx → thinking_sphinx}/association_spec.rb +1 -1
- data/spec/{lib/thinking_sphinx → thinking_sphinx}/attribute_spec.rb +87 -46
- data/spec/thinking_sphinx/auto_version_spec.rb +47 -0
- data/spec/{lib/thinking_sphinx → thinking_sphinx}/configuration_spec.rb +73 -63
- data/spec/thinking_sphinx/context_spec.rb +127 -0
- data/spec/{lib/thinking_sphinx → thinking_sphinx}/core/array_spec.rb +1 -1
- data/spec/{lib/thinking_sphinx → thinking_sphinx}/core/string_spec.rb +1 -1
- data/spec/{lib/thinking_sphinx → thinking_sphinx}/excerpter_spec.rb +1 -9
- data/spec/{lib/thinking_sphinx → thinking_sphinx}/facet_search_spec.rb +76 -82
- data/spec/{lib/thinking_sphinx → thinking_sphinx}/facet_spec.rb +5 -5
- data/spec/{lib/thinking_sphinx → thinking_sphinx}/field_spec.rb +1 -42
- data/spec/{lib/thinking_sphinx → thinking_sphinx}/index/builder_spec.rb +71 -31
- data/spec/{lib/thinking_sphinx → thinking_sphinx}/index/faux_column_spec.rb +8 -2
- data/spec/thinking_sphinx/index_spec.rb +183 -0
- data/spec/{lib/thinking_sphinx → thinking_sphinx}/rails_additions_spec.rb +5 -5
- data/spec/{lib/thinking_sphinx → thinking_sphinx}/search_methods_spec.rb +5 -1
- data/spec/{lib/thinking_sphinx → thinking_sphinx}/search_spec.rb +183 -31
- data/spec/{lib/thinking_sphinx → thinking_sphinx}/source_spec.rb +18 -2
- data/spec/thinking_sphinx/test_spec.rb +20 -0
- data/spec/thinking_sphinx_spec.rb +204 -0
- data/tasks/distribution.rb +7 -26
- data/tasks/testing.rb +32 -20
- metadata +488 -147
- data/VERSION.yml +0 -5
- data/features/datetime_deltas.feature +0 -66
- data/features/delayed_delta_indexing.feature +0 -37
- data/features/step_definitions/datetime_delta_steps.rb +0 -15
- data/features/step_definitions/delayed_delta_indexing_steps.rb +0 -7
- data/features/support/database.yml +0 -5
- data/features/support/db/active_record.rb +0 -40
- data/features/support/db/database.yml +0 -5
- data/features/support/db/fixtures/delayed_betas.rb +0 -10
- data/features/support/db/fixtures/thetas.rb +0 -10
- data/features/support/db/migrations/create_delayed_betas.rb +0 -17
- data/features/support/db/migrations/create_thetas.rb +0 -5
- data/features/support/db/mysql.rb +0 -3
- data/features/support/db/postgresql.rb +0 -3
- data/features/support/models/alpha.rb +0 -10
- data/features/support/models/delayed_beta.rb +0 -7
- data/features/support/models/theta.rb +0 -7
- data/features/support/post_database.rb +0 -43
- data/lib/thinking_sphinx/deltas/datetime_delta.rb +0 -50
- data/lib/thinking_sphinx/deltas/delayed_delta/delta_job.rb +0 -24
- data/lib/thinking_sphinx/deltas/delayed_delta/flag_as_deleted_job.rb +0 -27
- data/lib/thinking_sphinx/deltas/delayed_delta/job.rb +0 -26
- data/lib/thinking_sphinx/deltas/delayed_delta.rb +0 -30
- data/spec/lib/thinking_sphinx/active_record/scopes_spec.rb +0 -96
- data/spec/lib/thinking_sphinx/active_record_spec.rb +0 -353
- data/spec/lib/thinking_sphinx/deltas/job_spec.rb +0 -32
- data/spec/lib/thinking_sphinx/index_spec.rb +0 -45
- data/spec/lib/thinking_sphinx_spec.rb +0 -162
- data/vendor/after_commit/LICENSE +0 -20
- data/vendor/after_commit/README +0 -16
- data/vendor/after_commit/Rakefile +0 -22
- data/vendor/after_commit/init.rb +0 -8
- data/vendor/after_commit/lib/after_commit/active_record.rb +0 -114
- data/vendor/after_commit/lib/after_commit/connection_adapters.rb +0 -103
- data/vendor/after_commit/lib/after_commit.rb +0 -45
- data/vendor/after_commit/test/after_commit_test.rb +0 -53
- data/vendor/delayed_job/lib/delayed/job.rb +0 -251
- data/vendor/delayed_job/lib/delayed/message_sending.rb +0 -7
- data/vendor/delayed_job/lib/delayed/performable_method.rb +0 -55
- data/vendor/delayed_job/lib/delayed/worker.rb +0 -54
- data/vendor/riddle/lib/riddle/client/filter.rb +0 -53
- data/vendor/riddle/lib/riddle/client/message.rb +0 -66
- data/vendor/riddle/lib/riddle/client/response.rb +0 -84
- data/vendor/riddle/lib/riddle/client.rb +0 -635
- data/vendor/riddle/lib/riddle/configuration/distributed_index.rb +0 -48
- data/vendor/riddle/lib/riddle/configuration/index.rb +0 -142
- data/vendor/riddle/lib/riddle/configuration/indexer.rb +0 -19
- data/vendor/riddle/lib/riddle/configuration/remote_index.rb +0 -17
- data/vendor/riddle/lib/riddle/configuration/searchd.rb +0 -25
- data/vendor/riddle/lib/riddle/configuration/section.rb +0 -43
- data/vendor/riddle/lib/riddle/configuration/source.rb +0 -23
- data/vendor/riddle/lib/riddle/configuration/sql_source.rb +0 -34
- data/vendor/riddle/lib/riddle/configuration/xml_source.rb +0 -28
- data/vendor/riddle/lib/riddle/configuration.rb +0 -33
- data/vendor/riddle/lib/riddle/controller.rb +0 -53
- data/vendor/riddle/lib/riddle.rb +0 -30
- data/features/{support → thinking_sphinx}/database.example.yml +0 -0
- data/features/{support → thinking_sphinx}/db/fixtures/alphas.rb +0 -0
- data/features/{support → thinking_sphinx}/db/fixtures/authors.rb +0 -0
- data/features/{support → thinking_sphinx}/db/fixtures/boxes.rb +0 -0
- data/features/{support → thinking_sphinx}/db/fixtures/categories.rb +0 -0
- data/features/{support → thinking_sphinx}/db/fixtures/cats.rb +0 -0
- data/features/{support → thinking_sphinx}/db/fixtures/dogs.rb +0 -0
- data/features/{support → thinking_sphinx}/db/fixtures/extensible_betas.rb +0 -0
- data/features/{support → thinking_sphinx}/db/fixtures/gammas.rb +0 -0
- data/features/{support → thinking_sphinx}/db/fixtures/posts.rb +0 -0
- data/features/{support → thinking_sphinx}/db/fixtures/robots.rb +0 -0
- data/features/{support → thinking_sphinx}/db/migrations/create_animals.rb +0 -0
- data/features/{support → thinking_sphinx}/db/migrations/create_authors.rb +0 -0
- data/features/{support → thinking_sphinx}/db/migrations/create_authors_posts.rb +0 -0
- data/features/{support → thinking_sphinx}/db/migrations/create_betas.rb +0 -0
- data/features/{support → thinking_sphinx}/db/migrations/create_boxes.rb +0 -0
- data/features/{support → thinking_sphinx}/db/migrations/create_categories.rb +0 -0
- data/features/{support → thinking_sphinx}/db/migrations/create_comments.rb +0 -0
- data/features/{support → thinking_sphinx}/db/migrations/create_extensible_betas.rb +0 -0
- data/features/{support → thinking_sphinx}/db/migrations/create_gammas.rb +0 -0
- data/features/{support → thinking_sphinx}/db/migrations/create_people.rb +0 -0
- data/features/{support → thinking_sphinx}/db/migrations/create_posts.rb +0 -0
- data/features/{support → thinking_sphinx}/db/migrations/create_robots.rb +0 -0
- data/features/{support → thinking_sphinx}/db/migrations/create_taggings.rb +0 -0
- data/features/{support → thinking_sphinx}/db/migrations/create_tags.rb +0 -0
- data/features/{support → thinking_sphinx}/models/animal.rb +0 -0
- data/features/{support → thinking_sphinx}/models/author.rb +0 -0
- data/features/{support → thinking_sphinx}/models/box.rb +0 -0
- data/features/{support → thinking_sphinx}/models/cat.rb +0 -0
- data/features/{support → thinking_sphinx}/models/category.rb +0 -0
- data/features/{support → thinking_sphinx}/models/comment.rb +3 -3
- /data/features/{support → thinking_sphinx}/models/dog.rb +0 -0
- /data/features/{support → thinking_sphinx}/models/gamma.rb +0 -0
- /data/features/{support → thinking_sphinx}/models/robot.rb +0 -0
- /data/features/{support → thinking_sphinx}/models/tag.rb +0 -0
- /data/features/{support → thinking_sphinx}/models/tagging.rb +0 -0
|
@@ -12,7 +12,11 @@ module ThinkingSphinx
|
|
|
12
12
|
def self.included(base)
|
|
13
13
|
base.class_eval do
|
|
14
14
|
class_inheritable_array :sphinx_indexes, :sphinx_facets
|
|
15
|
+
|
|
16
|
+
extend ThinkingSphinx::ActiveRecord::ClassMethods
|
|
17
|
+
|
|
15
18
|
class << self
|
|
19
|
+
attr_accessor :sphinx_index_blocks
|
|
16
20
|
|
|
17
21
|
def set_sphinx_primary_key(attribute)
|
|
18
22
|
@sphinx_primary_key_attribute = attribute
|
|
@@ -22,93 +26,6 @@ module ThinkingSphinx
|
|
|
22
26
|
@sphinx_primary_key_attribute || primary_key
|
|
23
27
|
end
|
|
24
28
|
|
|
25
|
-
# Allows creation of indexes for Sphinx. If you don't do this, there
|
|
26
|
-
# isn't much point trying to search (or using this plugin at all,
|
|
27
|
-
# really).
|
|
28
|
-
#
|
|
29
|
-
# An example or two:
|
|
30
|
-
#
|
|
31
|
-
# define_index
|
|
32
|
-
# indexes :id, :as => :model_id
|
|
33
|
-
# indexes name
|
|
34
|
-
# end
|
|
35
|
-
#
|
|
36
|
-
# You can also grab fields from associations - multiple levels deep
|
|
37
|
-
# if necessary.
|
|
38
|
-
#
|
|
39
|
-
# define_index do
|
|
40
|
-
# indexes tags.name, :as => :tag
|
|
41
|
-
# indexes articles.content
|
|
42
|
-
# indexes orders.line_items.product.name, :as => :product
|
|
43
|
-
# end
|
|
44
|
-
#
|
|
45
|
-
# And it will automatically concatenate multiple fields:
|
|
46
|
-
#
|
|
47
|
-
# define_index do
|
|
48
|
-
# indexes [author.first_name, author.last_name], :as => :author
|
|
49
|
-
# end
|
|
50
|
-
#
|
|
51
|
-
# The #indexes method is for fields - if you want attributes, use
|
|
52
|
-
# #has instead. All the same rules apply - but keep in mind that
|
|
53
|
-
# attributes are for sorting, grouping and filtering, not searching.
|
|
54
|
-
#
|
|
55
|
-
# define_index do
|
|
56
|
-
# # fields ...
|
|
57
|
-
#
|
|
58
|
-
# has created_at, updated_at
|
|
59
|
-
# end
|
|
60
|
-
#
|
|
61
|
-
# One last feature is the delta index. This requires the model to
|
|
62
|
-
# have a boolean field named 'delta', and is enabled as follows:
|
|
63
|
-
#
|
|
64
|
-
# define_index do
|
|
65
|
-
# # fields ...
|
|
66
|
-
# # attributes ...
|
|
67
|
-
#
|
|
68
|
-
# set_property :delta => true
|
|
69
|
-
# end
|
|
70
|
-
#
|
|
71
|
-
# Check out the more detailed documentation for each of these methods
|
|
72
|
-
# at ThinkingSphinx::Index::Builder.
|
|
73
|
-
#
|
|
74
|
-
def define_index(&block)
|
|
75
|
-
return unless ThinkingSphinx.define_indexes?
|
|
76
|
-
|
|
77
|
-
self.sphinx_indexes ||= []
|
|
78
|
-
self.sphinx_facets ||= []
|
|
79
|
-
index = ThinkingSphinx::Index::Builder.generate(self, &block)
|
|
80
|
-
|
|
81
|
-
self.sphinx_indexes << index
|
|
82
|
-
unless ThinkingSphinx.indexed_models.include?(self.name)
|
|
83
|
-
ThinkingSphinx.indexed_models << self.name
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
if index.delta?
|
|
87
|
-
before_save :toggle_delta
|
|
88
|
-
after_commit :index_delta
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
after_destroy :toggle_deleted
|
|
92
|
-
|
|
93
|
-
include ThinkingSphinx::SearchMethods
|
|
94
|
-
include ThinkingSphinx::ActiveRecord::AttributeUpdates
|
|
95
|
-
include ThinkingSphinx::ActiveRecord::Scopes
|
|
96
|
-
|
|
97
|
-
index
|
|
98
|
-
|
|
99
|
-
# We want to make sure that if the database doesn't exist, then Thinking
|
|
100
|
-
# Sphinx doesn't mind when running non-TS tasks (like db:create, db:drop
|
|
101
|
-
# and db:migrate). It's a bit hacky, but I can't think of a better way.
|
|
102
|
-
rescue StandardError => err
|
|
103
|
-
case err.class.name
|
|
104
|
-
when "Mysql::Error", "Java::JavaSql::SQLException", "ActiveRecord::StatementInvalid"
|
|
105
|
-
return
|
|
106
|
-
else
|
|
107
|
-
raise err
|
|
108
|
-
end
|
|
109
|
-
end
|
|
110
|
-
alias_method :sphinx_index, :define_index
|
|
111
|
-
|
|
112
29
|
def sphinx_index_options
|
|
113
30
|
sphinx_indexes.last.options
|
|
114
31
|
end
|
|
@@ -127,26 +44,6 @@ module ThinkingSphinx
|
|
|
127
44
|
(subclasses << self).collect { |klass| klass.to_crc32 }
|
|
128
45
|
end
|
|
129
46
|
|
|
130
|
-
def source_of_sphinx_index
|
|
131
|
-
possible_models = self.sphinx_indexes.collect { |index| index.model }
|
|
132
|
-
return self if possible_models.include?(self)
|
|
133
|
-
|
|
134
|
-
parent = self.superclass
|
|
135
|
-
while !possible_models.include?(parent) && parent != ::ActiveRecord::Base
|
|
136
|
-
parent = parent.superclass
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
return parent
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
def to_riddle(offset)
|
|
143
|
-
sphinx_database_adapter.setup
|
|
144
|
-
|
|
145
|
-
indexes = [to_riddle_for_core(offset)]
|
|
146
|
-
indexes << to_riddle_for_delta(offset) if sphinx_delta?
|
|
147
|
-
indexes << to_riddle_for_distributed
|
|
148
|
-
end
|
|
149
|
-
|
|
150
47
|
def sphinx_database_adapter
|
|
151
48
|
@sphinx_database_adapter ||=
|
|
152
49
|
ThinkingSphinx::AbstractAdapter.detect(self)
|
|
@@ -156,86 +53,36 @@ module ThinkingSphinx
|
|
|
156
53
|
self.name.underscore.tr(':/\\', '_')
|
|
157
54
|
end
|
|
158
55
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
56
|
+
#
|
|
57
|
+
# The above method to_crc32s is dependant on the subclasses being loaded consistently
|
|
58
|
+
# After a reset_subclasses is called (during a Dispatcher.cleanup_application in development)
|
|
59
|
+
# Our subclasses will be lost but our context will not reload them for us.
|
|
60
|
+
#
|
|
61
|
+
# We reset the context which causes the subclasses to be reloaded next time the context is called.
|
|
62
|
+
#
|
|
63
|
+
def reset_subclasses_with_thinking_sphinx
|
|
64
|
+
reset_subclasses_without_thinking_sphinx
|
|
65
|
+
ThinkingSphinx.reset_context!
|
|
165
66
|
end
|
|
166
67
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
def sphinx_delta?
|
|
170
|
-
self.sphinx_indexes.any? { |index| index.delta? }
|
|
171
|
-
end
|
|
68
|
+
alias_method_chain :reset_subclasses, :thinking_sphinx
|
|
172
69
|
|
|
173
|
-
|
|
174
|
-
index = Riddle::Configuration::Index.new("#{sphinx_name}_core")
|
|
175
|
-
index.path = File.join(
|
|
176
|
-
ThinkingSphinx::Configuration.instance.searchd_file_path, index.name
|
|
177
|
-
)
|
|
178
|
-
|
|
179
|
-
set_configuration_options_for_indexes index
|
|
180
|
-
set_field_settings_for_indexes index
|
|
181
|
-
|
|
182
|
-
self.sphinx_indexes.select { |ts_index|
|
|
183
|
-
ts_index.model == self
|
|
184
|
-
}.each_with_index do |ts_index, i|
|
|
185
|
-
index.sources += ts_index.sources.collect { |source|
|
|
186
|
-
source.to_riddle_for_core(offset, i)
|
|
187
|
-
}
|
|
188
|
-
end
|
|
189
|
-
|
|
190
|
-
index
|
|
191
|
-
end
|
|
192
|
-
|
|
193
|
-
def to_riddle_for_delta(offset)
|
|
194
|
-
index = Riddle::Configuration::Index.new("#{sphinx_name}_delta")
|
|
195
|
-
index.parent = "#{sphinx_name}_core"
|
|
196
|
-
index.path = File.join(ThinkingSphinx::Configuration.instance.searchd_file_path, index.name)
|
|
197
|
-
|
|
198
|
-
self.sphinx_indexes.each_with_index do |ts_index, i|
|
|
199
|
-
index.sources += ts_index.sources.collect { |source|
|
|
200
|
-
source.to_riddle_for_delta(offset, i)
|
|
201
|
-
} if ts_index.delta?
|
|
202
|
-
end
|
|
203
|
-
|
|
204
|
-
index
|
|
205
|
-
end
|
|
70
|
+
private
|
|
206
71
|
|
|
207
|
-
def
|
|
208
|
-
|
|
209
|
-
index.local_indexes << "#{sphinx_name}_core"
|
|
210
|
-
index.local_indexes.unshift "#{sphinx_name}_delta" if sphinx_delta?
|
|
211
|
-
index
|
|
72
|
+
def defined_indexes?
|
|
73
|
+
@defined_indexes
|
|
212
74
|
end
|
|
213
75
|
|
|
214
|
-
def
|
|
215
|
-
|
|
216
|
-
index.send("#{key}=".to_sym, value)
|
|
217
|
-
end
|
|
218
|
-
|
|
219
|
-
self.sphinx_indexes.each do |ts_index|
|
|
220
|
-
ts_index.options.each do |key, value|
|
|
221
|
-
index.send("#{key}=".to_sym, value) if ThinkingSphinx::Configuration::IndexOptions.include?(key.to_s) && !value.nil?
|
|
222
|
-
end
|
|
223
|
-
end
|
|
76
|
+
def defined_indexes=(value)
|
|
77
|
+
@defined_indexes = value
|
|
224
78
|
end
|
|
225
79
|
|
|
226
|
-
def
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
self.sphinx_indexes.each do |ts_index|
|
|
230
|
-
index.prefix_field_names += ts_index.prefix_fields.collect(&field_names)
|
|
231
|
-
index.infix_field_names += ts_index.infix_fields.collect(&field_names)
|
|
232
|
-
end
|
|
80
|
+
def sphinx_delta?
|
|
81
|
+
self.sphinx_indexes.any? { |index| index.delta? }
|
|
233
82
|
end
|
|
234
83
|
end
|
|
235
84
|
end
|
|
236
85
|
|
|
237
|
-
base.send(:include, ThinkingSphinx::ActiveRecord::Delta)
|
|
238
|
-
|
|
239
86
|
::ActiveRecord::Associations::HasManyAssociation.send(
|
|
240
87
|
:include, ThinkingSphinx::ActiveRecord::HasManyAssociation
|
|
241
88
|
)
|
|
@@ -244,6 +91,241 @@ module ThinkingSphinx
|
|
|
244
91
|
)
|
|
245
92
|
end
|
|
246
93
|
|
|
94
|
+
module ClassMethods
|
|
95
|
+
# Allows creation of indexes for Sphinx. If you don't do this, there
|
|
96
|
+
# isn't much point trying to search (or using this plugin at all,
|
|
97
|
+
# really).
|
|
98
|
+
#
|
|
99
|
+
# An example or two:
|
|
100
|
+
#
|
|
101
|
+
# define_index
|
|
102
|
+
# indexes :id, :as => :model_id
|
|
103
|
+
# indexes name
|
|
104
|
+
# end
|
|
105
|
+
#
|
|
106
|
+
# You can also grab fields from associations - multiple levels deep
|
|
107
|
+
# if necessary.
|
|
108
|
+
#
|
|
109
|
+
# define_index do
|
|
110
|
+
# indexes tags.name, :as => :tag
|
|
111
|
+
# indexes articles.content
|
|
112
|
+
# indexes orders.line_items.product.name, :as => :product
|
|
113
|
+
# end
|
|
114
|
+
#
|
|
115
|
+
# And it will automatically concatenate multiple fields:
|
|
116
|
+
#
|
|
117
|
+
# define_index do
|
|
118
|
+
# indexes [author.first_name, author.last_name], :as => :author
|
|
119
|
+
# end
|
|
120
|
+
#
|
|
121
|
+
# The #indexes method is for fields - if you want attributes, use
|
|
122
|
+
# #has instead. All the same rules apply - but keep in mind that
|
|
123
|
+
# attributes are for sorting, grouping and filtering, not searching.
|
|
124
|
+
#
|
|
125
|
+
# define_index do
|
|
126
|
+
# # fields ...
|
|
127
|
+
#
|
|
128
|
+
# has created_at, updated_at
|
|
129
|
+
# end
|
|
130
|
+
#
|
|
131
|
+
# One last feature is the delta index. This requires the model to
|
|
132
|
+
# have a boolean field named 'delta', and is enabled as follows:
|
|
133
|
+
#
|
|
134
|
+
# define_index do
|
|
135
|
+
# # fields ...
|
|
136
|
+
# # attributes ...
|
|
137
|
+
#
|
|
138
|
+
# set_property :delta => true
|
|
139
|
+
# end
|
|
140
|
+
#
|
|
141
|
+
# Check out the more detailed documentation for each of these methods
|
|
142
|
+
# at ThinkingSphinx::Index::Builder.
|
|
143
|
+
#
|
|
144
|
+
def define_index(name = nil, &block)
|
|
145
|
+
self.sphinx_index_blocks ||= []
|
|
146
|
+
self.sphinx_indexes ||= []
|
|
147
|
+
self.sphinx_facets ||= []
|
|
148
|
+
|
|
149
|
+
ThinkingSphinx.context.add_indexed_model self
|
|
150
|
+
|
|
151
|
+
if sphinx_index_blocks.empty?
|
|
152
|
+
before_validation :define_indexes
|
|
153
|
+
before_destroy :define_indexes
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
self.sphinx_index_blocks << lambda {
|
|
157
|
+
add_sphinx_index name, &block
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
include ThinkingSphinx::ActiveRecord::Scopes
|
|
161
|
+
include ThinkingSphinx::SearchMethods
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def define_indexes
|
|
165
|
+
superclass.define_indexes unless superclass == ::ActiveRecord::Base
|
|
166
|
+
|
|
167
|
+
return if sphinx_index_blocks.nil? ||
|
|
168
|
+
defined_indexes? ||
|
|
169
|
+
!ThinkingSphinx.define_indexes?
|
|
170
|
+
|
|
171
|
+
sphinx_index_blocks.each do |block|
|
|
172
|
+
block.call
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
self.defined_indexes = true
|
|
176
|
+
|
|
177
|
+
# We want to make sure that if the database doesn't exist, then Thinking
|
|
178
|
+
# Sphinx doesn't mind when running non-TS tasks (like db:create, db:drop
|
|
179
|
+
# and db:migrate). It's a bit hacky, but I can't think of a better way.
|
|
180
|
+
rescue StandardError => err
|
|
181
|
+
case err.class.name
|
|
182
|
+
when "Mysql::Error", "Java::JavaSql::SQLException", "ActiveRecord::StatementInvalid"
|
|
183
|
+
return
|
|
184
|
+
else
|
|
185
|
+
raise err
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def add_sphinx_index(name, &block)
|
|
190
|
+
index = ThinkingSphinx::Index::Builder.generate self, name, &block
|
|
191
|
+
|
|
192
|
+
unless sphinx_indexes.any? { |i| i.name == index.name }
|
|
193
|
+
add_sphinx_callbacks_and_extend(index.delta?)
|
|
194
|
+
insert_sphinx_index index
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def insert_sphinx_index(index)
|
|
199
|
+
self.sphinx_indexes << index
|
|
200
|
+
subclasses.each { |klass| klass.insert_sphinx_index(index) }
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def has_sphinx_indexes?
|
|
204
|
+
sphinx_indexes &&
|
|
205
|
+
sphinx_index_blocks &&
|
|
206
|
+
(sphinx_indexes.length > 0 || sphinx_index_blocks.length > 0)
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def indexed_by_sphinx?
|
|
210
|
+
sphinx_indexes && sphinx_indexes.length > 0
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def delta_indexed_by_sphinx?
|
|
214
|
+
sphinx_indexes && sphinx_indexes.any? { |index| index.delta? }
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def sphinx_index_names
|
|
218
|
+
define_indexes
|
|
219
|
+
sphinx_indexes.collect(&:all_names).flatten
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
def core_index_names
|
|
223
|
+
define_indexes
|
|
224
|
+
sphinx_indexes.collect(&:core_name)
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
def delta_index_names
|
|
228
|
+
define_indexes
|
|
229
|
+
sphinx_indexes.select(&:delta?).collect(&:delta_name)
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
def to_riddle
|
|
233
|
+
define_indexes
|
|
234
|
+
sphinx_database_adapter.setup
|
|
235
|
+
|
|
236
|
+
local_sphinx_indexes.collect { |index|
|
|
237
|
+
index.to_riddle(sphinx_offset)
|
|
238
|
+
}.flatten
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
def source_of_sphinx_index
|
|
242
|
+
define_indexes
|
|
243
|
+
possible_models = self.sphinx_indexes.collect { |index| index.model }
|
|
244
|
+
return self if possible_models.include?(self)
|
|
245
|
+
|
|
246
|
+
parent = self.superclass
|
|
247
|
+
while !possible_models.include?(parent) && parent != ::ActiveRecord::Base
|
|
248
|
+
parent = parent.superclass
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
return parent
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
def delete_in_index(index, document_id)
|
|
255
|
+
return unless ThinkingSphinx.sphinx_running? &&
|
|
256
|
+
search_for_id(document_id, index)
|
|
257
|
+
|
|
258
|
+
ThinkingSphinx::Configuration.instance.client.update(
|
|
259
|
+
index, ['sphinx_deleted'], {document_id => [1]}
|
|
260
|
+
)
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
def sphinx_offset
|
|
264
|
+
ThinkingSphinx.context.superclass_indexed_models.
|
|
265
|
+
index eldest_indexed_ancestor
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
# Temporarily disable delta indexing inside a block, then perform a single
|
|
269
|
+
# rebuild of index at the end.
|
|
270
|
+
#
|
|
271
|
+
# Useful when performing updates to batches of models to prevent
|
|
272
|
+
# the delta index being rebuilt after each individual update.
|
|
273
|
+
#
|
|
274
|
+
# In the following example, the delta index will only be rebuilt once,
|
|
275
|
+
# not 10 times.
|
|
276
|
+
#
|
|
277
|
+
# SomeModel.suspended_delta do
|
|
278
|
+
# 10.times do
|
|
279
|
+
# SomeModel.create( ... )
|
|
280
|
+
# end
|
|
281
|
+
# end
|
|
282
|
+
#
|
|
283
|
+
def suspended_delta(reindex_after = true, &block)
|
|
284
|
+
define_indexes
|
|
285
|
+
original_setting = ThinkingSphinx.deltas_enabled?
|
|
286
|
+
ThinkingSphinx.deltas_enabled = false
|
|
287
|
+
begin
|
|
288
|
+
yield
|
|
289
|
+
ensure
|
|
290
|
+
ThinkingSphinx.deltas_enabled = original_setting
|
|
291
|
+
self.index_delta if reindex_after
|
|
292
|
+
end
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
private
|
|
296
|
+
|
|
297
|
+
def local_sphinx_indexes
|
|
298
|
+
sphinx_indexes.select { |index|
|
|
299
|
+
index.model == self
|
|
300
|
+
}
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
def add_sphinx_callbacks_and_extend(delta = false)
|
|
304
|
+
unless indexed_by_sphinx?
|
|
305
|
+
after_destroy :toggle_deleted
|
|
306
|
+
|
|
307
|
+
include ThinkingSphinx::ActiveRecord::AttributeUpdates
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
if delta && !delta_indexed_by_sphinx?
|
|
311
|
+
include ThinkingSphinx::ActiveRecord::Delta
|
|
312
|
+
|
|
313
|
+
before_save :toggle_delta
|
|
314
|
+
after_commit :index_delta
|
|
315
|
+
end
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
def eldest_indexed_ancestor
|
|
319
|
+
ancestors.reverse.detect { |ancestor|
|
|
320
|
+
ThinkingSphinx.context.indexed_models.include?(ancestor.name)
|
|
321
|
+
}.name
|
|
322
|
+
end
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
attr_accessor :excerpts
|
|
326
|
+
attr_accessor :sphinx_attributes
|
|
327
|
+
attr_accessor :matching_fields
|
|
328
|
+
|
|
247
329
|
def in_index?(suffix)
|
|
248
330
|
self.class.search_for_id self.sphinx_document_id, sphinx_index_name(suffix)
|
|
249
331
|
end
|
|
@@ -261,23 +343,15 @@ module ThinkingSphinx
|
|
|
261
343
|
end
|
|
262
344
|
|
|
263
345
|
def toggle_deleted
|
|
264
|
-
return unless ThinkingSphinx.updates_enabled?
|
|
265
|
-
|
|
266
|
-
config = ThinkingSphinx::Configuration.instance
|
|
267
|
-
client = Riddle::Client.new config.address, config.port
|
|
346
|
+
return unless ThinkingSphinx.updates_enabled?
|
|
268
347
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
348
|
+
self.class.core_index_names.each do |index_name|
|
|
349
|
+
self.class.delete_in_index index_name, self.sphinx_document_id
|
|
350
|
+
end
|
|
351
|
+
self.class.delta_index_names.each do |index_name|
|
|
352
|
+
self.class.delete_in_index index_name, self.sphinx_document_id
|
|
353
|
+
end if self.class.delta_indexed_by_sphinx? && toggled_delta?
|
|
274
354
|
|
|
275
|
-
client.update(
|
|
276
|
-
"#{self.class.sphinx_indexes.first.name}_delta",
|
|
277
|
-
['sphinx_deleted'],
|
|
278
|
-
{self.sphinx_document_id => 1}
|
|
279
|
-
) if self.class.sphinx_indexes.any? { |index| index.delta? } &&
|
|
280
|
-
self.toggled_delta?
|
|
281
355
|
rescue ::ThinkingSphinx::ConnectionError
|
|
282
356
|
# nothing
|
|
283
357
|
end
|
|
@@ -289,12 +363,12 @@ module ThinkingSphinx
|
|
|
289
363
|
# @return [Integer] Unique record id for the purposes of Sphinx.
|
|
290
364
|
#
|
|
291
365
|
def primary_key_for_sphinx
|
|
292
|
-
|
|
366
|
+
read_attribute(self.class.primary_key_for_sphinx)
|
|
293
367
|
end
|
|
294
368
|
|
|
295
369
|
def sphinx_document_id
|
|
296
|
-
primary_key_for_sphinx * ThinkingSphinx.indexed_models.size +
|
|
297
|
-
|
|
370
|
+
primary_key_for_sphinx * ThinkingSphinx.context.indexed_models.size +
|
|
371
|
+
self.class.sphinx_offset
|
|
298
372
|
end
|
|
299
373
|
|
|
300
374
|
private
|
|
@@ -302,5 +376,9 @@ module ThinkingSphinx
|
|
|
302
376
|
def sphinx_index_name(suffix)
|
|
303
377
|
"#{self.class.source_of_sphinx_index.name.underscore.tr(':/\\', '_')}_#{suffix}"
|
|
304
378
|
end
|
|
379
|
+
|
|
380
|
+
def define_indexes
|
|
381
|
+
self.class.define_indexes
|
|
382
|
+
end
|
|
305
383
|
end
|
|
306
384
|
end
|
|
@@ -10,22 +10,50 @@ module ThinkingSphinx
|
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
def self.detect(model)
|
|
13
|
+
adapter = adapter_for_model model
|
|
14
|
+
case adapter
|
|
15
|
+
when :mysql
|
|
16
|
+
ThinkingSphinx::MysqlAdapter.new model
|
|
17
|
+
when :postgresql
|
|
18
|
+
ThinkingSphinx::PostgreSQLAdapter.new model
|
|
19
|
+
else
|
|
20
|
+
raise "Invalid Database Adapter: Sphinx only supports MySQL and PostgreSQL, not #{adapter}"
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.adapter_for_model(model)
|
|
25
|
+
case ThinkingSphinx.database_adapter
|
|
26
|
+
when String
|
|
27
|
+
ThinkingSphinx.database_adapter.to_sym
|
|
28
|
+
when NilClass
|
|
29
|
+
standard_adapter_for_model model
|
|
30
|
+
when Proc
|
|
31
|
+
ThinkingSphinx.database_adapter.call model
|
|
32
|
+
else
|
|
33
|
+
ThinkingSphinx.database_adapter
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def self.standard_adapter_for_model(model)
|
|
13
38
|
case model.connection.class.name
|
|
14
39
|
when "ActiveRecord::ConnectionAdapters::MysqlAdapter",
|
|
15
|
-
"ActiveRecord::ConnectionAdapters::MysqlplusAdapter"
|
|
16
|
-
|
|
40
|
+
"ActiveRecord::ConnectionAdapters::MysqlplusAdapter",
|
|
41
|
+
"ActiveRecord::ConnectionAdapters::Mysql2Adapter",
|
|
42
|
+
"ActiveRecord::ConnectionAdapters::NullDBAdapter"
|
|
43
|
+
:mysql
|
|
17
44
|
when "ActiveRecord::ConnectionAdapters::PostgreSQLAdapter"
|
|
18
|
-
|
|
45
|
+
:postgresql
|
|
19
46
|
when "ActiveRecord::ConnectionAdapters::JdbcAdapter"
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
47
|
+
case model.connection.config[:adapter]
|
|
48
|
+
when "jdbcmysql"
|
|
49
|
+
:mysql
|
|
50
|
+
when "jdbcpostgresql"
|
|
51
|
+
:postgresql
|
|
24
52
|
else
|
|
25
|
-
|
|
53
|
+
model.connection.config[:adapter]
|
|
26
54
|
end
|
|
27
55
|
else
|
|
28
|
-
|
|
56
|
+
model.connection.class.name
|
|
29
57
|
end
|
|
30
58
|
end
|
|
31
59
|
|
|
@@ -33,6 +61,14 @@ module ThinkingSphinx
|
|
|
33
61
|
"#{@model.quoted_table_name}.#{@model.connection.quote_column_name(column)}"
|
|
34
62
|
end
|
|
35
63
|
|
|
64
|
+
def bigint_pattern
|
|
65
|
+
/bigint/i
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
def downcase(clause)
|
|
69
|
+
"LOWER(#{clause})"
|
|
70
|
+
end
|
|
71
|
+
|
|
36
72
|
protected
|
|
37
73
|
|
|
38
74
|
def connection
|
|
@@ -67,6 +67,10 @@ module ThinkingSphinx
|
|
|
67
67
|
"current_timestamp - interval '#{diff} seconds'"
|
|
68
68
|
end
|
|
69
69
|
|
|
70
|
+
def utc_query_pre
|
|
71
|
+
"SET TIME ZONE 'UTC'"
|
|
72
|
+
end
|
|
73
|
+
|
|
70
74
|
private
|
|
71
75
|
|
|
72
76
|
def execute(command, output_error = false)
|
|
@@ -115,6 +119,10 @@ module ThinkingSphinx
|
|
|
115
119
|
DECLARE j int;
|
|
116
120
|
DECLARE word_array bytea;
|
|
117
121
|
BEGIN
|
|
122
|
+
IF COALESCE(word, '') = '' THEN
|
|
123
|
+
return 0;
|
|
124
|
+
END IF;
|
|
125
|
+
|
|
118
126
|
i = 0;
|
|
119
127
|
tmp = 4294967295;
|
|
120
128
|
word_array = decode(replace(word, E'\\\\', E'\\\\\\\\'), 'escape');
|
|
@@ -135,7 +143,7 @@ module ThinkingSphinx
|
|
|
135
143
|
END LOOP;
|
|
136
144
|
return (tmp # 4294967295);
|
|
137
145
|
END
|
|
138
|
-
$$ IMMUTABLE
|
|
146
|
+
$$ IMMUTABLE LANGUAGE plpgsql;
|
|
139
147
|
SQL
|
|
140
148
|
execute function, true
|
|
141
149
|
end
|