thinking-sphinx 1.3.4 → 1.3.6
Sign up to get free protection for your applications and to get access to all the features.
- data/README.textile +15 -4
- data/VERSION +1 -0
- data/features/alternate_primary_key.feature +1 -1
- data/features/attribute_updates.feature +11 -5
- data/features/deleting_instances.feature +3 -0
- data/features/searching_by_index.feature +40 -0
- data/features/step_definitions/alpha_steps.rb +5 -1
- data/features/step_definitions/beta_steps.rb +1 -1
- data/features/step_definitions/common_steps.rb +12 -1
- data/features/step_definitions/sphinx_steps.rb +8 -4
- data/features/support/db/fixtures/tags.rb +1 -1
- data/features/support/env.rb +3 -0
- data/features/support/models/alpha.rb +11 -0
- data/lib/cucumber/thinking_sphinx/internal_world.rb +7 -6
- data/lib/thinking_sphinx.rb +40 -31
- data/lib/thinking_sphinx/active_record.rb +164 -195
- data/lib/thinking_sphinx/active_record/attribute_updates.rb +9 -6
- data/lib/thinking_sphinx/configuration.rb +1 -1
- data/lib/thinking_sphinx/deltas/default_delta.rb +14 -20
- data/lib/thinking_sphinx/index.rb +76 -19
- data/lib/thinking_sphinx/index/builder.rb +2 -2
- data/lib/thinking_sphinx/search.rb +7 -0
- data/lib/thinking_sphinx/search_methods.rb +22 -4
- data/lib/thinking_sphinx/source.rb +6 -6
- data/lib/thinking_sphinx/source/sql.rb +4 -2
- data/spec/{lib/thinking_sphinx → thinking_sphinx}/active_record/delta_spec.rb +4 -6
- data/spec/{lib/thinking_sphinx → thinking_sphinx}/active_record/has_many_association_spec.rb +0 -0
- data/spec/{lib/thinking_sphinx → thinking_sphinx}/active_record/scopes_spec.rb +0 -0
- data/spec/{lib/thinking_sphinx → thinking_sphinx}/active_record_spec.rb +254 -94
- data/spec/{lib/thinking_sphinx → thinking_sphinx}/association_spec.rb +0 -0
- data/spec/{lib/thinking_sphinx → thinking_sphinx}/attribute_spec.rb +0 -0
- data/spec/{lib/thinking_sphinx → thinking_sphinx}/configuration_spec.rb +2 -2
- data/spec/{lib/thinking_sphinx → thinking_sphinx}/core/array_spec.rb +0 -0
- data/spec/{lib/thinking_sphinx → thinking_sphinx}/core/string_spec.rb +0 -0
- data/spec/{lib/thinking_sphinx → thinking_sphinx}/excerpter_spec.rb +0 -0
- data/spec/{lib/thinking_sphinx → thinking_sphinx}/facet_search_spec.rb +0 -0
- data/spec/{lib/thinking_sphinx → thinking_sphinx}/facet_spec.rb +0 -0
- data/spec/{lib/thinking_sphinx → thinking_sphinx}/field_spec.rb +0 -0
- data/spec/{lib/thinking_sphinx → thinking_sphinx}/index/builder_spec.rb +10 -0
- data/spec/{lib/thinking_sphinx → thinking_sphinx}/index/faux_column_spec.rb +0 -0
- data/spec/thinking_sphinx/index_spec.rb +177 -0
- data/spec/{lib/thinking_sphinx → thinking_sphinx}/rails_additions_spec.rb +0 -0
- data/spec/{lib/thinking_sphinx → thinking_sphinx}/search_methods_spec.rb +0 -0
- data/spec/{lib/thinking_sphinx → thinking_sphinx}/search_spec.rb +33 -0
- data/spec/{lib/thinking_sphinx → thinking_sphinx}/source_spec.rb +0 -0
- data/spec/{lib/thinking_sphinx_spec.rb → thinking_sphinx_spec.rb} +62 -50
- data/tasks/distribution.rb +3 -3
- metadata +27 -31
- data/VERSION.yml +0 -5
- data/features/support/db/active_record.rb +0 -40
- data/features/support/db/database.yml +0 -5
- data/features/support/db/mysql.rb +0 -3
- data/features/support/db/postgresql.rb +0 -3
- data/features/support/post_database.rb +0 -43
- data/spec/lib/thinking_sphinx/index_spec.rb +0 -45
data/README.textile
CHANGED
@@ -14,16 +14,20 @@ To quickly see if your system is ready to run the thinking sphinx specs, run the
|
|
14
14
|
|
15
15
|
To get the spec suite running, you will need to install the ginger gem:
|
16
16
|
|
17
|
-
|
17
|
+
<pre><code>sudo gem install ginger --source http://gemcutter.org</code></pre>
|
18
18
|
|
19
|
-
Then install the cucumber, yard, jeweler and rspec gems. Make sure you have a git install version 1.6.0.0 or higher, otherwise the jeweler gem won't install.
|
19
|
+
Then install the cucumber, yard, jeweler and rspec gems. Make sure you have a git install version 1.6.0.0 or higher, otherwise the jeweler gem won't install. Bluecloth is required for some of the yard documentation.
|
20
20
|
|
21
|
-
|
21
|
+
<pre>
|
22
|
+
sudo gem install bluecloth cucumber yard jeweler rspec
|
23
|
+
</pre>
|
22
24
|
|
23
25
|
Then set up your database:
|
24
26
|
|
25
|
-
|
27
|
+
<pre>
|
28
|
+
cp spec/fixtures/database.yml.default spec/fixtures/database.yml &&
|
26
29
|
mysqladmin -u root create thinking_sphinx
|
30
|
+
</pre>
|
27
31
|
|
28
32
|
This last step can be done automatically by the contribute.rb script if all dependencies are met.
|
29
33
|
|
@@ -32,11 +36,15 @@ in the app root.
|
|
32
36
|
|
33
37
|
You should now have a passing test suite from which to build your patch on.
|
34
38
|
|
39
|
+
<pre>
|
35
40
|
rake spec
|
41
|
+
</pre>
|
36
42
|
|
37
43
|
If you get the message "Failed to start searchd daemon", run the spec with sudo:
|
38
44
|
|
45
|
+
<pre>
|
39
46
|
sudo rake spec
|
47
|
+
</pre>
|
40
48
|
|
41
49
|
If you quit the spec suite before it's completed, you may be left with data in the test
|
42
50
|
database, causing the next run to have failures. Let that run complete and then try again.
|
@@ -145,3 +153,6 @@ Since I first released this library, there's been quite a few people who have su
|
|
145
153
|
* Steve Madsen
|
146
154
|
* Justin DeWind
|
147
155
|
* Chris Z
|
156
|
+
* Chris Roos
|
157
|
+
* Andrew Assarattanakul
|
158
|
+
* Jonas von Andrian
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.3.6
|
@@ -9,15 +9,21 @@ Feature: Update attributes directly to Sphinx
|
|
9
9
|
When I filter by 3 on value
|
10
10
|
Then I should get 1 result
|
11
11
|
|
12
|
-
When I change the value of alpha
|
13
|
-
And I wait for Sphinx to catch up
|
12
|
+
When I change the value of alpha four to 13
|
13
|
+
And I wait for Sphinx to catch up
|
14
14
|
And I filter by 13 on value
|
15
|
+
And I use index alpha_core
|
16
|
+
Then I should get 1 result
|
17
|
+
When I use index alternative_core
|
15
18
|
Then I should get 1 result
|
16
19
|
|
17
|
-
When I change the value of alpha
|
18
|
-
And I wait for Sphinx to catch up
|
20
|
+
When I change the value of alpha four to 4
|
21
|
+
And I wait for Sphinx to catch up
|
19
22
|
And I filter by 13 on value
|
23
|
+
And I use index alpha_core
|
20
24
|
Then I should get 0 results
|
25
|
+
When I use index alternative_core
|
26
|
+
Then I should get 0 result
|
21
27
|
|
22
28
|
Scenario: Updating attributes in Sphinx with delta indexes
|
23
29
|
Given Sphinx is running
|
@@ -30,4 +36,4 @@ Feature: Update attributes directly to Sphinx
|
|
30
36
|
Then I should get 1 result
|
31
37
|
|
32
38
|
When I search for the document id of beta eight in the beta_delta index
|
33
|
-
Then it should not exist
|
39
|
+
Then it should not exist
|
@@ -56,6 +56,9 @@ Feature: Keeping Sphinx in line with deleted model instances
|
|
56
56
|
And I am searching on betas
|
57
57
|
When I create a new beta named thirteen
|
58
58
|
And I wait for Sphinx to catch up
|
59
|
+
And I search for thirteen
|
60
|
+
Then I should get 1 result
|
61
|
+
|
59
62
|
And I disable delta updates
|
60
63
|
And I destroy beta thirteen
|
61
64
|
And I wait for Sphinx to catch up
|
@@ -0,0 +1,40 @@
|
|
1
|
+
Feature: Searching within a single index
|
2
|
+
In order to use Thinking Sphinx's core functionality
|
3
|
+
A developer
|
4
|
+
Should be able to search on a single index
|
5
|
+
|
6
|
+
Scenario: Searching with alternative index
|
7
|
+
Given Sphinx is running
|
8
|
+
And I am searching on alphas
|
9
|
+
When I order by value
|
10
|
+
And I use index alternative_core
|
11
|
+
Then I should get 7 results
|
12
|
+
|
13
|
+
Scenario: Searching with default index
|
14
|
+
Given Sphinx is running
|
15
|
+
And I am searching on alphas
|
16
|
+
When I order by value
|
17
|
+
And I use index alpha_core
|
18
|
+
Then I should get 10 results
|
19
|
+
|
20
|
+
Scenario: Searching without specified index
|
21
|
+
Given Sphinx is running
|
22
|
+
And I am searching on alphas
|
23
|
+
When I order by value
|
24
|
+
Then I should get 10 results
|
25
|
+
|
26
|
+
Scenario: Deleting instances from the core index
|
27
|
+
Given Sphinx is running
|
28
|
+
And I am searching on alphas
|
29
|
+
|
30
|
+
When I create a new alpha named eleven
|
31
|
+
And I process the alpha_core index
|
32
|
+
And I process the alternative_core index
|
33
|
+
And I wait for Sphinx to catch up
|
34
|
+
And I search for eleven
|
35
|
+
Then I should get 1 result
|
36
|
+
|
37
|
+
When I destroy alpha eleven
|
38
|
+
And I wait for Sphinx to catch up
|
39
|
+
And I search for eleven
|
40
|
+
Then I should get 0 results
|
@@ -1,3 +1,7 @@
|
|
1
|
+
When /^I create a new alpha named (\w+)$/ do |name|
|
2
|
+
Alpha.create!(:name => name, :value => 101)
|
3
|
+
end
|
4
|
+
|
1
5
|
When /^I change the (\w+) of alpha (\w+) to (\w+)$/ do |column, name, replacement|
|
2
6
|
Alpha.find_by_name(name).update_attributes(column.to_sym => replacement)
|
3
|
-
end
|
7
|
+
end
|
@@ -10,6 +10,8 @@ Before do
|
|
10
10
|
@with_all = {}
|
11
11
|
@options = {}
|
12
12
|
@results = nil
|
13
|
+
|
14
|
+
Given "updates are enabled"
|
13
15
|
end
|
14
16
|
|
15
17
|
Given /^I am searching on (.+)$/ do |model|
|
@@ -25,6 +27,11 @@ When /^I am searching for ids$/ do
|
|
25
27
|
@method = :search_for_ids
|
26
28
|
end
|
27
29
|
|
30
|
+
When /^I use index (.+)$/ do |index|
|
31
|
+
@results = nil
|
32
|
+
@options[:index] = index
|
33
|
+
end
|
34
|
+
|
28
35
|
When /^I am retrieving the result count$/ do
|
29
36
|
@result = nil
|
30
37
|
@method = @model ? :search_count : :count
|
@@ -49,6 +56,10 @@ When /^I search for (\w+) on (\w+)$/ do |query, field|
|
|
49
56
|
@conditions[field.to_sym] = query
|
50
57
|
end
|
51
58
|
|
59
|
+
When /^I output the raw result data$/ do
|
60
|
+
puts results.results.inspect
|
61
|
+
end
|
62
|
+
|
52
63
|
When /^I clear existing filters$/ do
|
53
64
|
@with = {}
|
54
65
|
@without = {}
|
@@ -143,7 +154,7 @@ Then /^I can iterate by result and (\w+)$/ do |attribute|
|
|
143
154
|
iteration = lambda { |result, attr_value|
|
144
155
|
result.should be_kind_of(@model)
|
145
156
|
unless attribute == "group" && attr_value.nil?
|
146
|
-
attr_value.should be_kind_of(Integer)
|
157
|
+
attr_value.should be_kind_of(Integer)
|
147
158
|
end
|
148
159
|
}
|
149
160
|
|
@@ -18,6 +18,14 @@ When "I stop Sphinx" do
|
|
18
18
|
ThinkingSphinx::Configuration.instance.controller.stop
|
19
19
|
end
|
20
20
|
|
21
|
+
When /^I (enable|disable) delta updates$/ do |mode|
|
22
|
+
ThinkingSphinx.deltas_enabled = (mode == 'enable')
|
23
|
+
end
|
24
|
+
|
25
|
+
When /^I process the (\w+) index$/ do |index|
|
26
|
+
ThinkingSphinx::Configuration.instance.controller.index index
|
27
|
+
end
|
28
|
+
|
21
29
|
Then /^Sphinx should be running/ do
|
22
30
|
ThinkingSphinx.sphinx_running?.should be_true
|
23
31
|
end
|
@@ -25,7 +33,3 @@ end
|
|
25
33
|
Then "Sphinx should not be running" do
|
26
34
|
ThinkingSphinx.sphinx_running?.should be_false
|
27
35
|
end
|
28
|
-
|
29
|
-
When /^I (enable|disable) delta updates$/ do |mode|
|
30
|
-
ThinkingSphinx.deltas_enabled = (mode == 'enable')
|
31
|
-
end
|
data/features/support/env.rb
CHANGED
@@ -7,6 +7,9 @@ require 'will_paginate'
|
|
7
7
|
require 'active_record'
|
8
8
|
|
9
9
|
$:.unshift File.dirname(__FILE__) + '/../../lib'
|
10
|
+
Dir[File.join(File.dirname(__FILE__), '../../vendor/*/lib')].each do |path|
|
11
|
+
$:.unshift path
|
12
|
+
end
|
10
13
|
|
11
14
|
require 'cucumber/thinking_sphinx/internal_world'
|
12
15
|
|
@@ -7,4 +7,15 @@ class Alpha < ActiveRecord::Base
|
|
7
7
|
|
8
8
|
set_property :field_weights => {"name" => 10}
|
9
9
|
end
|
10
|
+
|
11
|
+
define_index 'alternative' do
|
12
|
+
indexes :name, :as => :alternative_name, :sortable => true
|
13
|
+
|
14
|
+
has value, created_at, created_on
|
15
|
+
has cost, :facet => true
|
16
|
+
|
17
|
+
set_property :field_weights => {'alternative_name' => 10}
|
18
|
+
|
19
|
+
where "value > 3"
|
20
|
+
end
|
10
21
|
end
|
@@ -79,11 +79,11 @@ module Cucumber
|
|
79
79
|
|
80
80
|
def database_settings
|
81
81
|
{
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
82
|
+
'adapter' => @adapter,
|
83
|
+
'database' => @database,
|
84
|
+
'username' => @username,
|
85
|
+
'password' => @password,
|
86
|
+
'host' => @host
|
87
87
|
}.merge yaml_database_settings
|
88
88
|
end
|
89
89
|
|
@@ -108,7 +108,8 @@ module Cucumber
|
|
108
108
|
end
|
109
109
|
|
110
110
|
def load_files(path)
|
111
|
-
Dir["#{path}/*.rb"].
|
111
|
+
files = Dir["#{path}/*.rb"].sort!
|
112
|
+
files.each do |file|
|
112
113
|
require file.gsub(/\.rb$/, '')
|
113
114
|
end
|
114
115
|
end
|
data/lib/thinking_sphinx.rb
CHANGED
@@ -52,26 +52,30 @@ module ThinkingSphinx
|
|
52
52
|
# @return [String] The version number as a string
|
53
53
|
#
|
54
54
|
def self.version
|
55
|
-
|
56
|
-
|
55
|
+
open(File.join(File.dirname(__FILE__), '../VERSION')) { |f|
|
56
|
+
f.read.strip
|
57
|
+
}
|
57
58
|
end
|
58
59
|
|
59
60
|
# The collection of indexed models. Keep in mind that Rails lazily loads
|
60
61
|
# its classes, so this may not actually be populated with _all_ the models
|
61
62
|
# that have Sphinx indexes.
|
62
63
|
def self.indexed_models
|
63
|
-
|
64
|
+
Thread.current[:thinking_sphinx_indexed_models] ||= []
|
64
65
|
end
|
65
66
|
|
66
67
|
def self.unique_id_expression(offset = nil)
|
67
|
-
"* #{
|
68
|
+
"* #{indexed_models.size} + #{offset || 0}"
|
68
69
|
end
|
69
70
|
|
70
71
|
# Check if index definition is disabled.
|
71
72
|
#
|
72
73
|
def self.define_indexes?
|
73
|
-
|
74
|
-
|
74
|
+
if Thread.current[:thinking_sphinx_define_indexes].nil?
|
75
|
+
Thread.current[:thinking_sphinx_define_indexes] = true
|
76
|
+
end
|
77
|
+
|
78
|
+
Thread.current[:thinking_sphinx_define_indexes]
|
75
79
|
end
|
76
80
|
|
77
81
|
# Enable/disable indexes - you may want to do this while migrating data.
|
@@ -79,16 +83,19 @@ module ThinkingSphinx
|
|
79
83
|
# ThinkingSphinx.define_indexes = false
|
80
84
|
#
|
81
85
|
def self.define_indexes=(value)
|
82
|
-
|
86
|
+
Thread.current[:thinking_sphinx_define_indexes] = value
|
83
87
|
end
|
84
88
|
|
85
|
-
@@deltas_enabled = nil
|
86
|
-
|
87
89
|
# Check if delta indexing is enabled.
|
88
90
|
#
|
89
91
|
def self.deltas_enabled?
|
90
|
-
|
91
|
-
|
92
|
+
if Thread.current[:thinking_sphinx_deltas_enabled].nil?
|
93
|
+
Thread.current[:thinking_sphinx_deltas_enabled] = (
|
94
|
+
ThinkingSphinx::Configuration.environment != "test"
|
95
|
+
)
|
96
|
+
end
|
97
|
+
|
98
|
+
Thread.current[:thinking_sphinx_deltas_enabled]
|
92
99
|
end
|
93
100
|
|
94
101
|
# Enable/disable all delta indexing.
|
@@ -96,17 +103,20 @@ module ThinkingSphinx
|
|
96
103
|
# ThinkingSphinx.deltas_enabled = false
|
97
104
|
#
|
98
105
|
def self.deltas_enabled=(value)
|
99
|
-
|
106
|
+
Thread.current[:thinking_sphinx_deltas_enabled] = value
|
100
107
|
end
|
101
108
|
|
102
|
-
@@updates_enabled = nil
|
103
|
-
|
104
109
|
# Check if updates are enabled. True by default, unless within the test
|
105
110
|
# environment.
|
106
111
|
#
|
107
112
|
def self.updates_enabled?
|
108
|
-
|
109
|
-
|
113
|
+
if Thread.current[:thinking_sphinx_updates_enabled].nil?
|
114
|
+
Thread.current[:thinking_sphinx_updates_enabled] = (
|
115
|
+
ThinkingSphinx::Configuration.environment != "test"
|
116
|
+
)
|
117
|
+
end
|
118
|
+
|
119
|
+
Thread.current[:thinking_sphinx_updates_enabled]
|
110
120
|
end
|
111
121
|
|
112
122
|
# Enable/disable updates to Sphinx
|
@@ -114,38 +124,37 @@ module ThinkingSphinx
|
|
114
124
|
# ThinkingSphinx.updates_enabled = false
|
115
125
|
#
|
116
126
|
def self.updates_enabled=(value)
|
117
|
-
|
127
|
+
Thread.current[:thinking_sphinx_updates_enabled] = value
|
118
128
|
end
|
119
129
|
|
120
|
-
@@suppress_delta_output = false
|
121
|
-
|
122
130
|
def self.suppress_delta_output?
|
123
|
-
|
131
|
+
Thread.current[:thinking_sphinx_suppress_delta_output] ||= false
|
124
132
|
end
|
125
133
|
|
126
134
|
def self.suppress_delta_output=(value)
|
127
|
-
|
135
|
+
Thread.current[:thinking_sphinx_suppress_delta_output] = value
|
128
136
|
end
|
129
137
|
|
130
|
-
@@use_group_by_shortcut = nil
|
131
138
|
# Checks to see if MySQL will allow simplistic GROUP BY statements. If not,
|
132
139
|
# or if not using MySQL, this will return false.
|
133
140
|
#
|
134
141
|
def self.use_group_by_shortcut?
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
142
|
+
if Thread.current[:thinking_sphinx_use_group_by_shortcut].nil?
|
143
|
+
Thread.current[:thinking_sphinx_use_group_by_shortcut] = !!(
|
144
|
+
mysql? && ::ActiveRecord::Base.connection.select_all(
|
145
|
+
"SELECT @@global.sql_mode, @@session.sql_mode;"
|
146
|
+
).all? { |key,value| value.nil? || value[/ONLY_FULL_GROUP_BY/].nil? }
|
147
|
+
)
|
148
|
+
end
|
149
|
+
|
150
|
+
Thread.current[:thinking_sphinx_use_group_by_shortcut]
|
140
151
|
end
|
141
152
|
|
142
|
-
@@remote_sphinx = false
|
143
|
-
|
144
153
|
# An indication of whether Sphinx is running on a remote machine instead of
|
145
154
|
# the same machine.
|
146
155
|
#
|
147
156
|
def self.remote_sphinx?
|
148
|
-
|
157
|
+
Thread.current[:thinking_sphinx_remote_sphinx] ||= false
|
149
158
|
end
|
150
159
|
|
151
160
|
# Tells Thinking Sphinx that Sphinx is running on a different machine, and
|
@@ -157,7 +166,7 @@ module ThinkingSphinx
|
|
157
166
|
# ThinkingSphinx.remote_sphinx = true
|
158
167
|
#
|
159
168
|
def self.remote_sphinx=(value)
|
160
|
-
|
169
|
+
Thread.current[:thinking_sphinx_remote_sphinx] = value
|
161
170
|
end
|
162
171
|
|
163
172
|
# Check if Sphinx is running. If remote_sphinx is set to true (indicating
|
@@ -12,8 +12,10 @@ 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
|
16
|
-
|
17
19
|
def set_sphinx_primary_key(attribute)
|
18
20
|
@sphinx_primary_key_attribute = attribute
|
19
21
|
end
|
@@ -22,93 +24,6 @@ module ThinkingSphinx
|
|
22
24
|
@sphinx_primary_key_attribute || primary_key
|
23
25
|
end
|
24
26
|
|
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
27
|
def sphinx_index_options
|
113
28
|
sphinx_indexes.last.options
|
114
29
|
end
|
@@ -127,26 +42,6 @@ module ThinkingSphinx
|
|
127
42
|
(subclasses << self).collect { |klass| klass.to_crc32 }
|
128
43
|
end
|
129
44
|
|
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
45
|
def sphinx_database_adapter
|
151
46
|
@sphinx_database_adapter ||=
|
152
47
|
ThinkingSphinx::AbstractAdapter.detect(self)
|
@@ -156,86 +51,14 @@ module ThinkingSphinx
|
|
156
51
|
self.name.underscore.tr(':/\\', '_')
|
157
52
|
end
|
158
53
|
|
159
|
-
def sphinx_index_names
|
160
|
-
klass = source_of_sphinx_index
|
161
|
-
names = ["#{klass.sphinx_name}_core"]
|
162
|
-
names << "#{klass.sphinx_name}_delta" if sphinx_delta?
|
163
|
-
|
164
|
-
names
|
165
|
-
end
|
166
|
-
|
167
54
|
private
|
168
55
|
|
169
56
|
def sphinx_delta?
|
170
57
|
self.sphinx_indexes.any? { |index| index.delta? }
|
171
58
|
end
|
172
|
-
|
173
|
-
def to_riddle_for_core(offset)
|
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
|
206
|
-
|
207
|
-
def to_riddle_for_distributed
|
208
|
-
index = Riddle::Configuration::DistributedIndex.new(sphinx_name)
|
209
|
-
index.local_indexes << "#{sphinx_name}_core"
|
210
|
-
index.local_indexes.unshift "#{sphinx_name}_delta" if sphinx_delta?
|
211
|
-
index
|
212
|
-
end
|
213
|
-
|
214
|
-
def set_configuration_options_for_indexes(index)
|
215
|
-
ThinkingSphinx::Configuration.instance.index_options.each do |key, value|
|
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
|
224
|
-
end
|
225
|
-
|
226
|
-
def set_field_settings_for_indexes(index)
|
227
|
-
field_names = lambda { |field| field.unique_name.to_s }
|
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
|
233
|
-
end
|
234
59
|
end
|
235
60
|
end
|
236
61
|
|
237
|
-
base.send(:include, ThinkingSphinx::ActiveRecord::Delta)
|
238
|
-
|
239
62
|
::ActiveRecord::Associations::HasManyAssociation.send(
|
240
63
|
:include, ThinkingSphinx::ActiveRecord::HasManyAssociation
|
241
64
|
)
|
@@ -244,6 +67,160 @@ module ThinkingSphinx
|
|
244
67
|
)
|
245
68
|
end
|
246
69
|
|
70
|
+
module ClassMethods
|
71
|
+
# Allows creation of indexes for Sphinx. If you don't do this, there
|
72
|
+
# isn't much point trying to search (or using this plugin at all,
|
73
|
+
# really).
|
74
|
+
#
|
75
|
+
# An example or two:
|
76
|
+
#
|
77
|
+
# define_index
|
78
|
+
# indexes :id, :as => :model_id
|
79
|
+
# indexes name
|
80
|
+
# end
|
81
|
+
#
|
82
|
+
# You can also grab fields from associations - multiple levels deep
|
83
|
+
# if necessary.
|
84
|
+
#
|
85
|
+
# define_index do
|
86
|
+
# indexes tags.name, :as => :tag
|
87
|
+
# indexes articles.content
|
88
|
+
# indexes orders.line_items.product.name, :as => :product
|
89
|
+
# end
|
90
|
+
#
|
91
|
+
# And it will automatically concatenate multiple fields:
|
92
|
+
#
|
93
|
+
# define_index do
|
94
|
+
# indexes [author.first_name, author.last_name], :as => :author
|
95
|
+
# end
|
96
|
+
#
|
97
|
+
# The #indexes method is for fields - if you want attributes, use
|
98
|
+
# #has instead. All the same rules apply - but keep in mind that
|
99
|
+
# attributes are for sorting, grouping and filtering, not searching.
|
100
|
+
#
|
101
|
+
# define_index do
|
102
|
+
# # fields ...
|
103
|
+
#
|
104
|
+
# has created_at, updated_at
|
105
|
+
# end
|
106
|
+
#
|
107
|
+
# One last feature is the delta index. This requires the model to
|
108
|
+
# have a boolean field named 'delta', and is enabled as follows:
|
109
|
+
#
|
110
|
+
# define_index do
|
111
|
+
# # fields ...
|
112
|
+
# # attributes ...
|
113
|
+
#
|
114
|
+
# set_property :delta => true
|
115
|
+
# end
|
116
|
+
#
|
117
|
+
# Check out the more detailed documentation for each of these methods
|
118
|
+
# at ThinkingSphinx::Index::Builder.
|
119
|
+
#
|
120
|
+
def define_index(name = nil, &block)
|
121
|
+
return unless ThinkingSphinx.define_indexes?
|
122
|
+
|
123
|
+
self.sphinx_indexes ||= []
|
124
|
+
self.sphinx_facets ||= []
|
125
|
+
|
126
|
+
index = ThinkingSphinx::Index::Builder.generate self, name, &block
|
127
|
+
|
128
|
+
unless ThinkingSphinx.indexed_models.include?(self.name)
|
129
|
+
ThinkingSphinx.indexed_models << self.name
|
130
|
+
end
|
131
|
+
|
132
|
+
add_sphinx_callbacks_and_extend(index.delta?)
|
133
|
+
self.sphinx_indexes << index
|
134
|
+
|
135
|
+
index
|
136
|
+
|
137
|
+
# We want to make sure that if the database doesn't exist, then Thinking
|
138
|
+
# Sphinx doesn't mind when running non-TS tasks (like db:create, db:drop
|
139
|
+
# and db:migrate). It's a bit hacky, but I can't think of a better way.
|
140
|
+
rescue StandardError => err
|
141
|
+
case err.class.name
|
142
|
+
when "Mysql::Error", "Java::JavaSql::SQLException", "ActiveRecord::StatementInvalid"
|
143
|
+
return
|
144
|
+
else
|
145
|
+
raise err
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def indexed_by_sphinx?
|
150
|
+
sphinx_indexes && sphinx_indexes.length > 0
|
151
|
+
end
|
152
|
+
|
153
|
+
def delta_indexed_by_sphinx?
|
154
|
+
sphinx_indexes && sphinx_indexes.any? { |index| index.delta? }
|
155
|
+
end
|
156
|
+
|
157
|
+
def sphinx_index_names
|
158
|
+
sphinx_indexes.collect(&:all_names).flatten
|
159
|
+
end
|
160
|
+
|
161
|
+
def core_index_names
|
162
|
+
sphinx_indexes.collect(&:core_name)
|
163
|
+
end
|
164
|
+
|
165
|
+
def delta_index_names
|
166
|
+
sphinx_indexes.select(&:delta?).collect(&:delta_name)
|
167
|
+
end
|
168
|
+
|
169
|
+
def to_riddle(offset)
|
170
|
+
sphinx_database_adapter.setup
|
171
|
+
|
172
|
+
local_sphinx_indexes.collect { |index|
|
173
|
+
index.to_riddle(offset)
|
174
|
+
}.flatten
|
175
|
+
end
|
176
|
+
|
177
|
+
def source_of_sphinx_index
|
178
|
+
possible_models = self.sphinx_indexes.collect { |index| index.model }
|
179
|
+
return self if possible_models.include?(self)
|
180
|
+
|
181
|
+
parent = self.superclass
|
182
|
+
while !possible_models.include?(parent) && parent != ::ActiveRecord::Base
|
183
|
+
parent = parent.superclass
|
184
|
+
end
|
185
|
+
|
186
|
+
return parent
|
187
|
+
end
|
188
|
+
|
189
|
+
def delete_in_index(index, document_id)
|
190
|
+
return unless ThinkingSphinx.sphinx_running? &&
|
191
|
+
search_for_id(document_id, index)
|
192
|
+
|
193
|
+
ThinkingSphinx::Configuration.instance.client.update(
|
194
|
+
index, ['sphinx_deleted'], {document_id => [1]}
|
195
|
+
)
|
196
|
+
end
|
197
|
+
|
198
|
+
private
|
199
|
+
|
200
|
+
def local_sphinx_indexes
|
201
|
+
sphinx_indexes.select { |index|
|
202
|
+
index.model == self
|
203
|
+
}
|
204
|
+
end
|
205
|
+
|
206
|
+
def add_sphinx_callbacks_and_extend(delta = false)
|
207
|
+
unless indexed_by_sphinx?
|
208
|
+
after_destroy :toggle_deleted
|
209
|
+
|
210
|
+
include ThinkingSphinx::SearchMethods
|
211
|
+
include ThinkingSphinx::ActiveRecord::AttributeUpdates
|
212
|
+
include ThinkingSphinx::ActiveRecord::Scopes
|
213
|
+
end
|
214
|
+
|
215
|
+
if delta && !delta_indexed_by_sphinx?
|
216
|
+
include ThinkingSphinx::ActiveRecord::Delta
|
217
|
+
|
218
|
+
before_save :toggle_delta
|
219
|
+
after_commit :index_delta
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
247
224
|
def in_index?(suffix)
|
248
225
|
self.class.search_for_id self.sphinx_document_id, sphinx_index_name(suffix)
|
249
226
|
end
|
@@ -261,23 +238,15 @@ module ThinkingSphinx
|
|
261
238
|
end
|
262
239
|
|
263
240
|
def toggle_deleted
|
264
|
-
return unless ThinkingSphinx.updates_enabled?
|
265
|
-
|
266
|
-
config = ThinkingSphinx::Configuration.instance
|
267
|
-
client = Riddle::Client.new config.address, config.port
|
241
|
+
return unless ThinkingSphinx.updates_enabled?
|
268
242
|
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
243
|
+
self.class.core_index_names.each do |index_name|
|
244
|
+
self.class.delete_in_index index_name, self.sphinx_document_id
|
245
|
+
end
|
246
|
+
self.class.delta_index_names.each do |index_name|
|
247
|
+
self.class.delete_in_index index_name, self.sphinx_document_id
|
248
|
+
end if self.class.delta_indexed_by_sphinx? && toggled_delta?
|
274
249
|
|
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
250
|
rescue ::ThinkingSphinx::ConnectionError
|
282
251
|
# nothing
|
283
252
|
end
|