sherpa99-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.
- data/LICENCE +20 -0
- data/README +107 -0
- data/README.textile +107 -0
- data/Rakefile +4 -0
- data/contribute.rb +328 -0
- data/cucumber.yml +1 -0
- data/features/a.rb +17 -0
- data/features/attribute_transformation.feature +22 -0
- data/features/datetime_deltas.feature +55 -0
- data/features/delayed_delta_indexing.feature +37 -0
- data/features/deleting_instances.feature +52 -0
- data/features/facets.feature +26 -0
- data/features/handling_edits.feature +67 -0
- data/features/retry_stale_indexes.feature +24 -0
- data/features/searching_across_models.feature +20 -0
- data/features/searching_by_model.feature +118 -0
- data/features/searching_with_find_arguments.feature +56 -0
- data/features/sphinx_detection.feature +16 -0
- data/features/step_definitions/alpha_steps.rb +3 -0
- data/features/step_definitions/beta_steps.rb +11 -0
- data/features/step_definitions/cat_steps.rb +3 -0
- data/features/step_definitions/common_steps.rb +154 -0
- data/features/step_definitions/datetime_delta_steps.rb +11 -0
- data/features/step_definitions/delayed_delta_indexing_steps.rb +7 -0
- data/features/step_definitions/facet_steps.rb +30 -0
- data/features/step_definitions/find_arguments_steps.rb +36 -0
- data/features/step_definitions/gamma_steps.rb +15 -0
- data/features/step_definitions/search_steps.rb +66 -0
- data/features/step_definitions/sphinx_steps.rb +23 -0
- data/features/support/db/active_record.rb +40 -0
- data/features/support/db/database.example.yml +4 -0
- data/features/support/db/migrations/create_alphas.rb +18 -0
- data/features/support/db/migrations/create_animals.rb +9 -0
- data/features/support/db/migrations/create_betas.rb +15 -0
- data/features/support/db/migrations/create_boxes.rb +13 -0
- data/features/support/db/migrations/create_comments.rb +13 -0
- data/features/support/db/migrations/create_delayed_betas.rb +28 -0
- data/features/support/db/migrations/create_developers.rb +39 -0
- data/features/support/db/migrations/create_gammas.rb +14 -0
- data/features/support/db/migrations/create_people.rb +1014 -0
- data/features/support/db/migrations/create_posts.rb +6 -0
- data/features/support/db/migrations/create_thetas.rb +16 -0
- data/features/support/db/mysql.rb +4 -0
- data/features/support/db/postgresql.rb +4 -0
- data/features/support/env.rb +6 -0
- data/features/support/models/alpha.rb +9 -0
- data/features/support/models/animal.rb +5 -0
- data/features/support/models/beta.rb +7 -0
- data/features/support/models/box.rb +8 -0
- data/features/support/models/cat.rb +3 -0
- data/features/support/models/comment.rb +3 -0
- data/features/support/models/delayed_beta.rb +7 -0
- data/features/support/models/developer.rb +8 -0
- data/features/support/models/gamma.rb +5 -0
- data/features/support/models/person.rb +8 -0
- data/features/support/models/post.rb +8 -0
- data/features/support/models/theta.rb +7 -0
- data/features/support/post_database.rb +37 -0
- data/features/support/z.rb +19 -0
- data/ginger_scenarios.rb +24 -0
- data/init.rb +12 -0
- data/lib/thinking_sphinx.rb +144 -0
- data/lib/thinking_sphinx/active_record.rb +245 -0
- data/lib/thinking_sphinx/active_record/delta.rb +74 -0
- data/lib/thinking_sphinx/active_record/has_many_association.rb +29 -0
- data/lib/thinking_sphinx/active_record/search.rb +57 -0
- data/lib/thinking_sphinx/adapters/abstract_adapter.rb +34 -0
- data/lib/thinking_sphinx/adapters/mysql_adapter.rb +53 -0
- data/lib/thinking_sphinx/adapters/postgresql_adapter.rb +129 -0
- data/lib/thinking_sphinx/association.rb +144 -0
- data/lib/thinking_sphinx/attribute.rb +258 -0
- data/lib/thinking_sphinx/collection.rb +142 -0
- data/lib/thinking_sphinx/configuration.rb +236 -0
- data/lib/thinking_sphinx/core/string.rb +22 -0
- data/lib/thinking_sphinx/deltas.rb +22 -0
- data/lib/thinking_sphinx/deltas/datetime_delta.rb +50 -0
- data/lib/thinking_sphinx/deltas/default_delta.rb +65 -0
- data/lib/thinking_sphinx/deltas/delayed_delta.rb +25 -0
- data/lib/thinking_sphinx/deltas/delayed_delta/delta_job.rb +24 -0
- data/lib/thinking_sphinx/deltas/delayed_delta/flag_as_deleted_job.rb +27 -0
- data/lib/thinking_sphinx/deltas/delayed_delta/job.rb +26 -0
- data/lib/thinking_sphinx/facet.rb +58 -0
- data/lib/thinking_sphinx/facet_collection.rb +44 -0
- data/lib/thinking_sphinx/field.rb +172 -0
- data/lib/thinking_sphinx/index.rb +414 -0
- data/lib/thinking_sphinx/index/builder.rb +233 -0
- data/lib/thinking_sphinx/index/faux_column.rb +110 -0
- data/lib/thinking_sphinx/rails_additions.rb +133 -0
- data/lib/thinking_sphinx/search.rb +638 -0
- data/lib/thinking_sphinx/tasks.rb +128 -0
- data/rails/init.rb +6 -0
- data/spec/fixtures/data.sql +32 -0
- data/spec/fixtures/database.yml.default +3 -0
- data/spec/fixtures/models.rb +81 -0
- data/spec/fixtures/structure.sql +84 -0
- data/spec/spec_helper.rb +54 -0
- data/spec/sphinx_helper.rb +109 -0
- data/spec/unit/thinking_sphinx/active_record/delta_spec.rb +136 -0
- data/spec/unit/thinking_sphinx/active_record/has_many_association_spec.rb +53 -0
- data/spec/unit/thinking_sphinx/active_record/search_spec.rb +107 -0
- data/spec/unit/thinking_sphinx/active_record_spec.rb +256 -0
- data/spec/unit/thinking_sphinx/association_spec.rb +247 -0
- data/spec/unit/thinking_sphinx/attribute_spec.rb +212 -0
- data/spec/unit/thinking_sphinx/collection_spec.rb +14 -0
- data/spec/unit/thinking_sphinx/configuration_spec.rb +136 -0
- data/spec/unit/thinking_sphinx/core/string_spec.rb +9 -0
- data/spec/unit/thinking_sphinx/field_spec.rb +145 -0
- data/spec/unit/thinking_sphinx/index/builder_spec.rb +5 -0
- data/spec/unit/thinking_sphinx/index/faux_column_spec.rb +30 -0
- data/spec/unit/thinking_sphinx/index_spec.rb +54 -0
- data/spec/unit/thinking_sphinx/search_spec.rb +59 -0
- data/spec/unit/thinking_sphinx_spec.rb +129 -0
- data/tasks/distribution.rb +48 -0
- data/tasks/rails.rake +1 -0
- data/tasks/testing.rb +86 -0
- data/thinking-sphinx.gemspec +232 -0
- data/vendor/after_commit/LICENSE +20 -0
- data/vendor/after_commit/README +16 -0
- data/vendor/after_commit/Rakefile +22 -0
- data/vendor/after_commit/init.rb +5 -0
- data/vendor/after_commit/lib/after_commit.rb +42 -0
- data/vendor/after_commit/lib/after_commit/active_record.rb +91 -0
- data/vendor/after_commit/lib/after_commit/connection_adapters.rb +103 -0
- data/vendor/after_commit/test/after_commit_test.rb +53 -0
- data/vendor/delayed_job/lib/delayed/job.rb +251 -0
- data/vendor/delayed_job/lib/delayed/message_sending.rb +7 -0
- data/vendor/delayed_job/lib/delayed/performable_method.rb +55 -0
- data/vendor/delayed_job/lib/delayed/worker.rb +54 -0
- data/vendor/riddle/lib/riddle.rb +30 -0
- data/vendor/riddle/lib/riddle/client.rb +619 -0
- data/vendor/riddle/lib/riddle/client/filter.rb +53 -0
- data/vendor/riddle/lib/riddle/client/message.rb +65 -0
- data/vendor/riddle/lib/riddle/client/response.rb +84 -0
- data/vendor/riddle/lib/riddle/configuration.rb +33 -0
- data/vendor/riddle/lib/riddle/configuration/distributed_index.rb +48 -0
- data/vendor/riddle/lib/riddle/configuration/index.rb +142 -0
- data/vendor/riddle/lib/riddle/configuration/indexer.rb +19 -0
- data/vendor/riddle/lib/riddle/configuration/remote_index.rb +17 -0
- data/vendor/riddle/lib/riddle/configuration/searchd.rb +25 -0
- data/vendor/riddle/lib/riddle/configuration/section.rb +37 -0
- data/vendor/riddle/lib/riddle/configuration/source.rb +23 -0
- data/vendor/riddle/lib/riddle/configuration/sql_source.rb +34 -0
- data/vendor/riddle/lib/riddle/configuration/xml_source.rb +28 -0
- data/vendor/riddle/lib/riddle/controller.rb +44 -0
- metadata +248 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
ActiveRecord::Base.connection.create_table :thetas, :force => true do |t|
|
|
2
|
+
t.column :name, :string, :null => false
|
|
3
|
+
t.column :created_at, :datetime, :null => false
|
|
4
|
+
t.column :updated_at, :datetime, :null => false
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
Theta.create :name => "one"
|
|
8
|
+
Theta.create :name => "two"
|
|
9
|
+
Theta.create :name => "three"
|
|
10
|
+
Theta.create :name => "four"
|
|
11
|
+
Theta.create :name => "five"
|
|
12
|
+
Theta.create :name => "six"
|
|
13
|
+
Theta.create :name => "seven"
|
|
14
|
+
Theta.create :name => "eight"
|
|
15
|
+
Theta.create :name => "nine"
|
|
16
|
+
Theta.create :name => "ten"
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
$:.unshift File.dirname(__FILE__) + '/../../lib'
|
|
2
|
+
|
|
3
|
+
require 'lib/thinking_sphinx'
|
|
4
|
+
|
|
5
|
+
%w( tmp/config tmp/log tmp/db/sphinx/development ).each do |path|
|
|
6
|
+
FileUtils.mkdir_p "#{Dir.pwd}/#{path}"
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
Kernel.const_set :RAILS_ROOT, "#{Dir.pwd}/tmp" unless defined?(RAILS_ROOT)
|
|
10
|
+
|
|
11
|
+
at_exit do
|
|
12
|
+
ThinkingSphinx::Configuration.instance.controller.stop
|
|
13
|
+
sleep(1) # Ensure Sphinx has shut down completely
|
|
14
|
+
FileUtils.rm_r "#{Dir.pwd}/tmp"
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Add log file
|
|
18
|
+
ActiveRecord::Base.logger = Logger.new open("tmp/active_record.log", "a")
|
|
19
|
+
|
|
20
|
+
ThinkingSphinx.deltas_enabled = false
|
|
21
|
+
|
|
22
|
+
# Load Models
|
|
23
|
+
Dir["features/support/models/*.rb"].sort.each do |file|
|
|
24
|
+
require file.gsub(/\.rb$/, '')
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Set up database tables and records
|
|
28
|
+
Dir["features/support/db/migrations/*.rb"].each do |file|
|
|
29
|
+
require file.gsub(/\.rb$/, '')
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
ThinkingSphinx.deltas_enabled = true
|
|
33
|
+
ThinkingSphinx.suppress_delta_output = true
|
|
34
|
+
|
|
35
|
+
ThinkingSphinx::Configuration.instance.build
|
|
36
|
+
ThinkingSphinx::Configuration.instance.controller.index
|
|
37
|
+
ThinkingSphinx::Configuration.instance.controller.start
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# This file exists because Cucumber likes to auto-load all ruby files
|
|
2
|
+
puts <<-MESSAGE
|
|
3
|
+
Cucumber 0.1.13 defaults to loading all ruby files within the features folder,
|
|
4
|
+
with something approaching reverse-alphabetical order, and preferring the
|
|
5
|
+
features/support folder over everything else. This is annoying, because some
|
|
6
|
+
files need to be loaded before others (and others perhaps not at all, given
|
|
7
|
+
missing dependencies). Hence this place-holder imaginatively named 'z.rb', to
|
|
8
|
+
force this message.
|
|
9
|
+
|
|
10
|
+
A work-around is to use cucumber profiles. You will find the default profile in
|
|
11
|
+
cucumber.yml should serve your needs fine, unless you add new step definitions.
|
|
12
|
+
When you do that, you can regenerate the YAML file by running:
|
|
13
|
+
rake cucumber_defaults
|
|
14
|
+
|
|
15
|
+
And then run specific features as follows is slightly more verbose, but it
|
|
16
|
+
works, whereas this doesn't.
|
|
17
|
+
cucumber -p default features/something.feature
|
|
18
|
+
MESSAGE
|
|
19
|
+
exit 0
|
data/ginger_scenarios.rb
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
require 'ginger'
|
|
2
|
+
|
|
3
|
+
Ginger.configure do |config|
|
|
4
|
+
config.aliases["active_record"] = "activerecord"
|
|
5
|
+
config.aliases["active_support"] = "activesupport"
|
|
6
|
+
|
|
7
|
+
ar_1_2_6 = Ginger::Scenario.new
|
|
8
|
+
ar_1_2_6[/^active_?support$/] = "1.4.4"
|
|
9
|
+
ar_1_2_6[/^active_?record$/] = "1.15.6"
|
|
10
|
+
|
|
11
|
+
ar_2_0_4 = Ginger::Scenario.new
|
|
12
|
+
ar_2_0_4[/^active_?support$/] = "2.0.4"
|
|
13
|
+
ar_2_0_4[/^active_?record$/] = "2.0.4"
|
|
14
|
+
|
|
15
|
+
ar_2_1_2 = Ginger::Scenario.new
|
|
16
|
+
ar_2_1_2[/^active_?support$/] = "2.1.2"
|
|
17
|
+
ar_2_1_2[/^active_?record$/] = "2.1.2"
|
|
18
|
+
|
|
19
|
+
ar_2_2_0 = Ginger::Scenario.new
|
|
20
|
+
ar_2_2_0[/^active_?support$/] = "2.2.0"
|
|
21
|
+
ar_2_2_0[/^active_?record$/] = "2.2.0"
|
|
22
|
+
|
|
23
|
+
config.scenarios << ar_1_2_6 << ar_2_0_4 << ar_2_1_2 << ar_2_2_0
|
|
24
|
+
end
|
data/init.rb
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
require 'thinking_sphinx'
|
|
2
|
+
|
|
3
|
+
if Rails::VERSION::STRING.to_f < 2.1
|
|
4
|
+
ThinkingSphinx::Configuration.instance.load_models
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
if Rails::VERSION::STRING.to_f > 1.2
|
|
8
|
+
require 'action_controller/dispatcher'
|
|
9
|
+
ActionController::Dispatcher.to_prepare :thinking_sphinx do
|
|
10
|
+
ThinkingSphinx::Configuration.instance.load_models
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
Dir[File.join(File.dirname(__FILE__), '../vendor/*/lib')].each do |path|
|
|
2
|
+
$LOAD_PATH.unshift path
|
|
3
|
+
end
|
|
4
|
+
|
|
5
|
+
require 'active_record'
|
|
6
|
+
require 'riddle'
|
|
7
|
+
require 'after_commit'
|
|
8
|
+
|
|
9
|
+
require 'thinking_sphinx/core/string'
|
|
10
|
+
require 'thinking_sphinx/active_record'
|
|
11
|
+
require 'thinking_sphinx/association'
|
|
12
|
+
require 'thinking_sphinx/attribute'
|
|
13
|
+
require 'thinking_sphinx/collection'
|
|
14
|
+
require 'thinking_sphinx/configuration'
|
|
15
|
+
require 'thinking_sphinx/facet'
|
|
16
|
+
require 'thinking_sphinx/facet_collection'
|
|
17
|
+
require 'thinking_sphinx/field'
|
|
18
|
+
require 'thinking_sphinx/index'
|
|
19
|
+
require 'thinking_sphinx/rails_additions'
|
|
20
|
+
require 'thinking_sphinx/search'
|
|
21
|
+
require 'thinking_sphinx/deltas'
|
|
22
|
+
|
|
23
|
+
require 'thinking_sphinx/adapters/abstract_adapter'
|
|
24
|
+
require 'thinking_sphinx/adapters/mysql_adapter'
|
|
25
|
+
require 'thinking_sphinx/adapters/postgresql_adapter'
|
|
26
|
+
|
|
27
|
+
ActiveRecord::Base.send(:include, ThinkingSphinx::ActiveRecord)
|
|
28
|
+
|
|
29
|
+
Merb::Plugins.add_rakefiles(
|
|
30
|
+
File.join(File.dirname(__FILE__), "thinking_sphinx", "tasks")
|
|
31
|
+
) if defined?(Merb)
|
|
32
|
+
|
|
33
|
+
module ThinkingSphinx
|
|
34
|
+
module Version #:nodoc:
|
|
35
|
+
Major = 1
|
|
36
|
+
Minor = 1
|
|
37
|
+
Tiny = 3
|
|
38
|
+
|
|
39
|
+
String = [Major, Minor, Tiny].join('.')
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# A ConnectionError will get thrown when a connection to Sphinx can't be
|
|
43
|
+
# made.
|
|
44
|
+
class ConnectionError < StandardError
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# A StaleIdsException is thrown by Collection.instances_from_matches if there
|
|
48
|
+
# are records in Sphinx but not in the database, so the search can be retried.
|
|
49
|
+
class StaleIdsException < StandardError
|
|
50
|
+
attr_accessor :ids
|
|
51
|
+
def initialize(ids)
|
|
52
|
+
self.ids = ids
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# The collection of indexed models. Keep in mind that Rails lazily loads
|
|
57
|
+
# its classes, so this may not actually be populated with _all_ the models
|
|
58
|
+
# that have Sphinx indexes.
|
|
59
|
+
def self.indexed_models
|
|
60
|
+
@@indexed_models ||= []
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Check if index definition is disabled.
|
|
64
|
+
#
|
|
65
|
+
def self.define_indexes?
|
|
66
|
+
@@define_indexes = true unless defined?(@@define_indexes)
|
|
67
|
+
@@define_indexes == true
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Enable/disable indexes - you may want to do this while migrating data.
|
|
71
|
+
#
|
|
72
|
+
# ThinkingSphinx.define_indexes = false
|
|
73
|
+
#
|
|
74
|
+
def self.define_indexes=(value)
|
|
75
|
+
@@define_indexes = value
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
@@deltas_enabled = nil
|
|
79
|
+
|
|
80
|
+
# Check if delta indexing is enabled.
|
|
81
|
+
#
|
|
82
|
+
def self.deltas_enabled?
|
|
83
|
+
@@deltas_enabled = (ThinkingSphinx::Configuration.environment != 'test') if @@deltas_enabled.nil?
|
|
84
|
+
@@deltas_enabled
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Enable/disable all delta indexing.
|
|
88
|
+
#
|
|
89
|
+
# ThinkingSphinx.deltas_enabled = false
|
|
90
|
+
#
|
|
91
|
+
def self.deltas_enabled=(value)
|
|
92
|
+
@@deltas_enabled = value
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
@@updates_enabled = nil
|
|
96
|
+
|
|
97
|
+
# Check if updates are enabled. True by default, unless within the test
|
|
98
|
+
# environment.
|
|
99
|
+
#
|
|
100
|
+
def self.updates_enabled?
|
|
101
|
+
@@updates_enabled = (ThinkingSphinx::Configuration.environment != 'test') if @@updates_enabled.nil?
|
|
102
|
+
@@updates_enabled
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Enable/disable updates to Sphinx
|
|
106
|
+
#
|
|
107
|
+
# ThinkingSphinx.updates_enabled = false
|
|
108
|
+
#
|
|
109
|
+
def self.updates_enabled=(value)
|
|
110
|
+
@@updates_enabled = value
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
@@suppress_delta_output = false
|
|
114
|
+
|
|
115
|
+
def self.suppress_delta_output?
|
|
116
|
+
@@suppress_delta_output
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def self.suppress_delta_output=(value)
|
|
120
|
+
@@suppress_delta_output = value
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# Checks to see if MySQL will allow simplistic GROUP BY statements. If not,
|
|
124
|
+
# or if not using MySQL, this will return false.
|
|
125
|
+
#
|
|
126
|
+
def self.use_group_by_shortcut?
|
|
127
|
+
::ActiveRecord::ConnectionAdapters.constants.include?("MysqlAdapter") &&
|
|
128
|
+
::ActiveRecord::Base.connection.is_a?(
|
|
129
|
+
::ActiveRecord::ConnectionAdapters::MysqlAdapter
|
|
130
|
+
) &&
|
|
131
|
+
::ActiveRecord::Base.connection.select_all(
|
|
132
|
+
"SELECT @@global.sql_mode, @@session.sql_mode;"
|
|
133
|
+
).all? { |key,value| value.nil? || value[/ONLY_FULL_GROUP_BY/].nil? }
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def self.sphinx_running?
|
|
137
|
+
!!sphinx_pid
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def self.sphinx_pid
|
|
141
|
+
pid_file = ThinkingSphinx::Configuration.instance.pid_file
|
|
142
|
+
`cat #{pid_file}`[/\d+/] if File.exists?(pid_file)
|
|
143
|
+
end
|
|
144
|
+
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
|