thinking-sphinx 1.3.4 → 1.3.6
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 +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
|