ebeigarts-thinking-sphinx 1.1.22 → 1.2.10
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 +14 -0
- data/VERSION.yml +4 -0
- data/lib/thinking_sphinx.rb +60 -64
- data/lib/thinking_sphinx/active_record.rb +35 -7
- data/lib/thinking_sphinx/active_record/scopes.rb +39 -0
- data/lib/thinking_sphinx/adapters/postgresql_adapter.rb +3 -2
- data/lib/thinking_sphinx/attribute.rb +62 -22
- data/lib/thinking_sphinx/configuration.rb +21 -1
- data/lib/thinking_sphinx/core/array.rb +7 -0
- data/lib/thinking_sphinx/deltas/delayed_delta.rb +3 -0
- data/lib/thinking_sphinx/deploy/capistrano.rb +26 -8
- data/lib/thinking_sphinx/excerpter.rb +22 -0
- data/lib/thinking_sphinx/facet.rb +8 -2
- data/lib/thinking_sphinx/facet_search.rb +134 -0
- data/lib/thinking_sphinx/index.rb +2 -2
- data/lib/thinking_sphinx/index/builder.rb +0 -1
- data/lib/thinking_sphinx/property.rb +2 -0
- data/lib/thinking_sphinx/rails_additions.rb +14 -0
- data/lib/thinking_sphinx/search.rb +633 -671
- data/lib/thinking_sphinx/search_methods.rb +421 -0
- data/lib/thinking_sphinx/source.rb +5 -5
- data/lib/thinking_sphinx/source/internal_properties.rb +1 -1
- data/lib/thinking_sphinx/source/sql.rb +10 -8
- data/lib/thinking_sphinx/tasks.rb +14 -9
- data/spec/{unit → lib}/thinking_sphinx/active_record/delta_spec.rb +1 -1
- data/spec/{unit → lib}/thinking_sphinx/active_record/has_many_association_spec.rb +0 -0
- data/spec/lib/thinking_sphinx/active_record/scopes_spec.rb +96 -0
- data/spec/{unit → lib}/thinking_sphinx/active_record_spec.rb +44 -5
- data/spec/{unit → lib}/thinking_sphinx/association_spec.rb +0 -0
- data/spec/{unit → lib}/thinking_sphinx/attribute_spec.rb +110 -3
- data/spec/{unit → lib}/thinking_sphinx/configuration_spec.rb +87 -41
- data/spec/lib/thinking_sphinx/core/array_spec.rb +9 -0
- data/spec/{unit → lib}/thinking_sphinx/core/string_spec.rb +0 -0
- data/spec/lib/thinking_sphinx/excerpter_spec.rb +49 -0
- data/spec/lib/thinking_sphinx/facet_search_spec.rb +176 -0
- data/spec/{unit → lib}/thinking_sphinx/facet_spec.rb +34 -15
- data/spec/{unit → lib}/thinking_sphinx/field_spec.rb +0 -0
- data/spec/{unit → lib}/thinking_sphinx/index/builder_spec.rb +100 -0
- data/spec/{unit → lib}/thinking_sphinx/index/faux_column_spec.rb +0 -0
- data/spec/{unit → lib}/thinking_sphinx/index_spec.rb +0 -0
- data/spec/{unit → lib}/thinking_sphinx/rails_additions_spec.rb +12 -0
- data/spec/lib/thinking_sphinx/search_methods_spec.rb +152 -0
- data/spec/lib/thinking_sphinx/search_spec.rb +1066 -0
- data/spec/{unit → lib}/thinking_sphinx/source_spec.rb +10 -0
- data/spec/{unit → lib}/thinking_sphinx_spec.rb +10 -0
- data/tasks/distribution.rb +20 -38
- data/tasks/testing.rb +3 -1
- data/vendor/riddle/lib/riddle.rb +1 -1
- data/vendor/riddle/lib/riddle/client.rb +3 -0
- data/vendor/riddle/lib/riddle/client/message.rb +4 -3
- data/vendor/riddle/lib/riddle/configuration/section.rb +1 -1
- data/vendor/riddle/lib/riddle/controller.rb +17 -7
- metadata +63 -83
- data/lib/thinking_sphinx/active_record/search.rb +0 -57
- data/lib/thinking_sphinx/collection.rb +0 -148
- data/lib/thinking_sphinx/facet_collection.rb +0 -59
- data/lib/thinking_sphinx/search/facets.rb +0 -104
- data/spec/unit/thinking_sphinx/active_record/search_spec.rb +0 -107
- data/spec/unit/thinking_sphinx/collection_spec.rb +0 -15
- data/spec/unit/thinking_sphinx/facet_collection_spec.rb +0 -64
- data/spec/unit/thinking_sphinx/search_spec.rb +0 -228
data/README.textile
CHANGED
|
@@ -30,6 +30,10 @@ Alternatively, install the ginger gem directly from the freelancing-god github r
|
|
|
30
30
|
|
|
31
31
|
sudo gem sources -a http://gems.github.com
|
|
32
32
|
sudo gem install freelancing-god-ginger
|
|
33
|
+
|
|
34
|
+
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.
|
|
35
|
+
|
|
36
|
+
sudo gem install cucumber yard jeweler rspec
|
|
33
37
|
|
|
34
38
|
Then set up your database:
|
|
35
39
|
|
|
@@ -141,3 +145,13 @@ Since I first released this library, there's been quite a few people who have su
|
|
|
141
145
|
* Paul Campbell
|
|
142
146
|
* Matthew Beale
|
|
143
147
|
* Tom Simnett
|
|
148
|
+
* Erik Ostrom
|
|
149
|
+
* Ole Riesenberg
|
|
150
|
+
* Josh Kalderimis
|
|
151
|
+
* J.D. Hollis
|
|
152
|
+
* Jeffrey Chupp
|
|
153
|
+
* Rob Anderton
|
|
154
|
+
* Zach Inglis
|
|
155
|
+
* Ward Bekker
|
|
156
|
+
* Brian Terlson
|
|
157
|
+
* Christian Aust
|
data/VERSION.yml
ADDED
data/lib/thinking_sphinx.rb
CHANGED
|
@@ -5,22 +5,25 @@ end
|
|
|
5
5
|
require 'active_record'
|
|
6
6
|
require 'riddle'
|
|
7
7
|
require 'after_commit'
|
|
8
|
+
require 'yaml'
|
|
8
9
|
|
|
10
|
+
require 'thinking_sphinx/core/array'
|
|
9
11
|
require 'thinking_sphinx/core/string'
|
|
10
12
|
require 'thinking_sphinx/property'
|
|
11
13
|
require 'thinking_sphinx/active_record'
|
|
12
14
|
require 'thinking_sphinx/association'
|
|
13
15
|
require 'thinking_sphinx/attribute'
|
|
14
|
-
require 'thinking_sphinx/collection'
|
|
15
16
|
require 'thinking_sphinx/configuration'
|
|
17
|
+
require 'thinking_sphinx/excerpter'
|
|
16
18
|
require 'thinking_sphinx/facet'
|
|
17
19
|
require 'thinking_sphinx/class_facet'
|
|
18
|
-
require 'thinking_sphinx/
|
|
20
|
+
require 'thinking_sphinx/facet_search'
|
|
19
21
|
require 'thinking_sphinx/field'
|
|
20
22
|
require 'thinking_sphinx/index'
|
|
21
23
|
require 'thinking_sphinx/source'
|
|
22
24
|
require 'thinking_sphinx/rails_additions'
|
|
23
25
|
require 'thinking_sphinx/search'
|
|
26
|
+
require 'thinking_sphinx/search_methods'
|
|
24
27
|
require 'thinking_sphinx/deltas'
|
|
25
28
|
|
|
26
29
|
require 'thinking_sphinx/adapters/abstract_adapter'
|
|
@@ -36,19 +39,11 @@ Merb::Plugins.add_rakefiles(
|
|
|
36
39
|
) if defined?(Merb)
|
|
37
40
|
|
|
38
41
|
module ThinkingSphinx
|
|
39
|
-
module Version #:nodoc:
|
|
40
|
-
Major = 1
|
|
41
|
-
Minor = 1
|
|
42
|
-
Tiny = 22
|
|
43
|
-
|
|
44
|
-
String = [Major, Minor, Tiny].join('.')
|
|
45
|
-
end
|
|
46
|
-
|
|
47
42
|
# A ConnectionError will get thrown when a connection to Sphinx can't be
|
|
48
43
|
# made.
|
|
49
44
|
class ConnectionError < StandardError
|
|
50
45
|
end
|
|
51
|
-
|
|
46
|
+
|
|
52
47
|
# A StaleIdsException is thrown by Collection.instances_from_matches if there
|
|
53
48
|
# are records in Sphinx but not in the database, so the search can be retried.
|
|
54
49
|
class StaleIdsException < StandardError
|
|
@@ -58,41 +53,50 @@ module ThinkingSphinx
|
|
|
58
53
|
end
|
|
59
54
|
end
|
|
60
55
|
|
|
56
|
+
# The current version of Thinking Sphinx.
|
|
57
|
+
#
|
|
58
|
+
# @return [String] The version number as a string
|
|
59
|
+
#
|
|
60
|
+
def self.version
|
|
61
|
+
hash = YAML.load_file File.join(File.dirname(__FILE__), '../VERSION.yml')
|
|
62
|
+
[hash[:major], hash[:minor], hash[:patch]].join('.')
|
|
63
|
+
end
|
|
64
|
+
|
|
61
65
|
# The collection of indexed models. Keep in mind that Rails lazily loads
|
|
62
66
|
# its classes, so this may not actually be populated with _all_ the models
|
|
63
67
|
# that have Sphinx indexes.
|
|
64
68
|
def self.indexed_models
|
|
65
69
|
@@indexed_models ||= []
|
|
66
70
|
end
|
|
67
|
-
|
|
71
|
+
|
|
68
72
|
def self.unique_id_expression(offset = nil)
|
|
69
73
|
"* #{ThinkingSphinx.indexed_models.size} + #{offset || 0}"
|
|
70
74
|
end
|
|
71
|
-
|
|
75
|
+
|
|
72
76
|
# Check if index definition is disabled.
|
|
73
|
-
#
|
|
77
|
+
#
|
|
74
78
|
def self.define_indexes?
|
|
75
79
|
@@define_indexes = true unless defined?(@@define_indexes)
|
|
76
80
|
@@define_indexes == true
|
|
77
81
|
end
|
|
78
|
-
|
|
82
|
+
|
|
79
83
|
# Enable/disable indexes - you may want to do this while migrating data.
|
|
80
|
-
#
|
|
84
|
+
#
|
|
81
85
|
# ThinkingSphinx.define_indexes = false
|
|
82
|
-
#
|
|
86
|
+
#
|
|
83
87
|
def self.define_indexes=(value)
|
|
84
88
|
@@define_indexes = value
|
|
85
89
|
end
|
|
86
|
-
|
|
90
|
+
|
|
87
91
|
@@deltas_enabled = nil
|
|
88
92
|
|
|
89
93
|
# Check if delta indexing is enabled.
|
|
90
|
-
#
|
|
94
|
+
#
|
|
91
95
|
def self.deltas_enabled?
|
|
92
96
|
@@deltas_enabled = (ThinkingSphinx::Configuration.environment != 'test') if @@deltas_enabled.nil?
|
|
93
97
|
@@deltas_enabled
|
|
94
98
|
end
|
|
95
|
-
|
|
99
|
+
|
|
96
100
|
# Enable/disable all delta indexing.
|
|
97
101
|
#
|
|
98
102
|
# ThinkingSphinx.deltas_enabled = false
|
|
@@ -100,38 +104,38 @@ module ThinkingSphinx
|
|
|
100
104
|
def self.deltas_enabled=(value)
|
|
101
105
|
@@deltas_enabled = value
|
|
102
106
|
end
|
|
103
|
-
|
|
107
|
+
|
|
104
108
|
@@updates_enabled = nil
|
|
105
|
-
|
|
109
|
+
|
|
106
110
|
# Check if updates are enabled. True by default, unless within the test
|
|
107
111
|
# environment.
|
|
108
|
-
#
|
|
112
|
+
#
|
|
109
113
|
def self.updates_enabled?
|
|
110
114
|
@@updates_enabled = (ThinkingSphinx::Configuration.environment != 'test') if @@updates_enabled.nil?
|
|
111
115
|
@@updates_enabled
|
|
112
116
|
end
|
|
113
|
-
|
|
117
|
+
|
|
114
118
|
# Enable/disable updates to Sphinx
|
|
115
|
-
#
|
|
119
|
+
#
|
|
116
120
|
# ThinkingSphinx.updates_enabled = false
|
|
117
121
|
#
|
|
118
122
|
def self.updates_enabled=(value)
|
|
119
123
|
@@updates_enabled = value
|
|
120
124
|
end
|
|
121
|
-
|
|
125
|
+
|
|
122
126
|
@@suppress_delta_output = false
|
|
123
|
-
|
|
127
|
+
|
|
124
128
|
def self.suppress_delta_output?
|
|
125
129
|
@@suppress_delta_output
|
|
126
130
|
end
|
|
127
|
-
|
|
131
|
+
|
|
128
132
|
def self.suppress_delta_output=(value)
|
|
129
133
|
@@suppress_delta_output = value
|
|
130
134
|
end
|
|
131
|
-
|
|
135
|
+
|
|
132
136
|
# Checks to see if MySQL will allow simplistic GROUP BY statements. If not,
|
|
133
137
|
# or if not using MySQL, this will return false.
|
|
134
|
-
#
|
|
138
|
+
#
|
|
135
139
|
def self.use_group_by_shortcut?
|
|
136
140
|
!!(
|
|
137
141
|
mysql? && ::ActiveRecord::Base.connection.select_all(
|
|
@@ -139,79 +143,71 @@ module ThinkingSphinx
|
|
|
139
143
|
).all? { |key,value| value.nil? || value[/ONLY_FULL_GROUP_BY/].nil? }
|
|
140
144
|
)
|
|
141
145
|
end
|
|
142
|
-
|
|
146
|
+
|
|
143
147
|
@@remote_sphinx = false
|
|
144
|
-
|
|
148
|
+
|
|
145
149
|
# An indication of whether Sphinx is running on a remote machine instead of
|
|
146
150
|
# the same machine.
|
|
147
|
-
#
|
|
151
|
+
#
|
|
148
152
|
def self.remote_sphinx?
|
|
149
153
|
@@remote_sphinx
|
|
150
154
|
end
|
|
151
|
-
|
|
155
|
+
|
|
152
156
|
# Tells Thinking Sphinx that Sphinx is running on a different machine, and
|
|
153
|
-
# thus it can't reliably guess whether it is running or not (ie: the
|
|
157
|
+
# thus it can't reliably guess whether it is running or not (ie: the
|
|
154
158
|
# #sphinx_running? method), and so just assumes it is.
|
|
155
|
-
#
|
|
159
|
+
#
|
|
156
160
|
# Useful for multi-machine deployments. Set it in your production.rb file.
|
|
157
|
-
#
|
|
161
|
+
#
|
|
158
162
|
# ThinkingSphinx.remote_sphinx = true
|
|
159
|
-
#
|
|
163
|
+
#
|
|
160
164
|
def self.remote_sphinx=(value)
|
|
161
165
|
@@remote_sphinx = value
|
|
162
166
|
end
|
|
163
|
-
|
|
167
|
+
|
|
164
168
|
# Check if Sphinx is running. If remote_sphinx is set to true (indicating
|
|
165
169
|
# Sphinx is on a different machine), this will always return true, and you
|
|
166
170
|
# will have to handle any connection errors yourself.
|
|
167
|
-
#
|
|
171
|
+
#
|
|
168
172
|
def self.sphinx_running?
|
|
169
173
|
remote_sphinx? || sphinx_running_by_pid?
|
|
170
174
|
end
|
|
171
|
-
|
|
175
|
+
|
|
172
176
|
# Check if Sphinx is actually running, provided the pid is on the same
|
|
173
177
|
# machine as this code.
|
|
174
|
-
#
|
|
178
|
+
#
|
|
175
179
|
def self.sphinx_running_by_pid?
|
|
176
180
|
!!sphinx_pid && pid_active?(sphinx_pid)
|
|
177
181
|
end
|
|
178
|
-
|
|
182
|
+
|
|
179
183
|
def self.sphinx_pid
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
if microsoft?
|
|
185
|
-
pid_file.gsub!('/', '\\')
|
|
186
|
-
cat_command = 'type'
|
|
184
|
+
if File.exists?(ThinkingSphinx::Configuration.instance.pid_file)
|
|
185
|
+
File.read(ThinkingSphinx::Configuration.instance.pid_file)[/\d+/]
|
|
186
|
+
else
|
|
187
|
+
nil
|
|
187
188
|
end
|
|
188
|
-
|
|
189
|
-
`#{cat_command} #{pid_file}`[/\d+/]
|
|
190
189
|
end
|
|
191
|
-
|
|
190
|
+
|
|
192
191
|
def self.pid_active?(pid)
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
# In JRuby this returns -1 if the process doesn't exist
|
|
197
|
-
Process.getpgid(pid.to_i) != -1
|
|
198
|
-
rescue Exception => e
|
|
199
|
-
false
|
|
200
|
-
end
|
|
192
|
+
!!Process.kill(0, pid.to_i)
|
|
193
|
+
rescue Exception => e
|
|
194
|
+
false
|
|
201
195
|
end
|
|
202
|
-
|
|
196
|
+
|
|
203
197
|
def self.microsoft?
|
|
204
198
|
RUBY_PLATFORM =~ /mswin/
|
|
205
199
|
end
|
|
206
|
-
|
|
200
|
+
|
|
207
201
|
def self.jruby?
|
|
208
202
|
defined?(JRUBY_VERSION)
|
|
209
203
|
end
|
|
210
|
-
|
|
204
|
+
|
|
211
205
|
def self.mysql?
|
|
212
206
|
::ActiveRecord::Base.connection.class.name.demodulize == "MysqlAdapter" ||
|
|
213
207
|
::ActiveRecord::Base.connection.class.name.demodulize == "MysqlplusAdapter" || (
|
|
214
208
|
jruby? && ::ActiveRecord::Base.connection.config[:adapter] == "jdbcmysql"
|
|
215
209
|
)
|
|
216
210
|
end
|
|
211
|
+
|
|
212
|
+
extend ThinkingSphinx::SearchMethods::ClassMethods
|
|
217
213
|
end
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
require 'thinking_sphinx/active_record/attribute_updates'
|
|
2
2
|
require 'thinking_sphinx/active_record/delta'
|
|
3
|
-
require 'thinking_sphinx/active_record/search'
|
|
4
3
|
require 'thinking_sphinx/active_record/has_many_association'
|
|
4
|
+
require 'thinking_sphinx/active_record/scopes'
|
|
5
5
|
|
|
6
6
|
module ThinkingSphinx
|
|
7
7
|
# Core additions to ActiveRecord models - define_index for creating indexes
|
|
@@ -13,6 +13,15 @@ module ThinkingSphinx
|
|
|
13
13
|
base.class_eval do
|
|
14
14
|
class_inheritable_array :sphinx_indexes, :sphinx_facets
|
|
15
15
|
class << self
|
|
16
|
+
|
|
17
|
+
def set_sphinx_primary_key(attribute)
|
|
18
|
+
@sphinx_primary_key_attribute = attribute
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def primary_key_for_sphinx
|
|
22
|
+
@sphinx_primary_key_attribute || primary_key
|
|
23
|
+
end
|
|
24
|
+
|
|
16
25
|
# Allows creation of indexes for Sphinx. If you don't do this, there
|
|
17
26
|
# isn't much point trying to search (or using this plugin at all,
|
|
18
27
|
# really).
|
|
@@ -80,7 +89,9 @@ module ThinkingSphinx
|
|
|
80
89
|
|
|
81
90
|
after_destroy :toggle_deleted
|
|
82
91
|
|
|
92
|
+
include ThinkingSphinx::SearchMethods
|
|
83
93
|
include ThinkingSphinx::ActiveRecord::AttributeUpdates
|
|
94
|
+
include ThinkingSphinx::ActiveRecord::Scopes
|
|
84
95
|
|
|
85
96
|
index
|
|
86
97
|
|
|
@@ -140,12 +151,20 @@ module ThinkingSphinx
|
|
|
140
151
|
ThinkingSphinx::AbstractAdapter.detect(self)
|
|
141
152
|
end
|
|
142
153
|
|
|
143
|
-
private
|
|
144
|
-
|
|
145
154
|
def sphinx_name
|
|
146
155
|
self.name.underscore.tr(':/\\', '_')
|
|
147
156
|
end
|
|
148
157
|
|
|
158
|
+
def sphinx_index_names
|
|
159
|
+
klass = source_of_sphinx_index
|
|
160
|
+
names = ["#{klass.sphinx_name}_core"]
|
|
161
|
+
names << "#{klass.sphinx_name}_delta" if sphinx_delta?
|
|
162
|
+
|
|
163
|
+
names
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
private
|
|
167
|
+
|
|
149
168
|
def sphinx_delta?
|
|
150
169
|
self.sphinx_indexes.any? { |index| index.delta? }
|
|
151
170
|
end
|
|
@@ -215,7 +234,6 @@ module ThinkingSphinx
|
|
|
215
234
|
end
|
|
216
235
|
|
|
217
236
|
base.send(:include, ThinkingSphinx::ActiveRecord::Delta)
|
|
218
|
-
base.send(:include, ThinkingSphinx::ActiveRecord::Search)
|
|
219
237
|
|
|
220
238
|
::ActiveRecord::Associations::HasManyAssociation.send(
|
|
221
239
|
:include, ThinkingSphinx::ActiveRecord::HasManyAssociation
|
|
@@ -264,13 +282,23 @@ module ThinkingSphinx
|
|
|
264
282
|
# nothing
|
|
265
283
|
end
|
|
266
284
|
|
|
285
|
+
# Returns the unique integer id for the object. This method uses the
|
|
286
|
+
# attribute hash to get around ActiveRecord always mapping the #id method
|
|
287
|
+
# to whatever the real primary key is (which may be a unique string hash).
|
|
288
|
+
#
|
|
289
|
+
# @return [Integer] Unique record id for the purposes of Sphinx.
|
|
290
|
+
#
|
|
291
|
+
def primary_key_for_sphinx
|
|
292
|
+
@primary_key_for_sphinx ||= read_attribute(self.class.primary_key_for_sphinx)
|
|
293
|
+
end
|
|
294
|
+
|
|
267
295
|
def sphinx_document_id
|
|
268
|
-
|
|
296
|
+
primary_key_for_sphinx * ThinkingSphinx.indexed_models.size +
|
|
269
297
|
ThinkingSphinx.indexed_models.index(self.class.source_of_sphinx_index.name)
|
|
270
298
|
end
|
|
271
|
-
|
|
299
|
+
|
|
272
300
|
private
|
|
273
|
-
|
|
301
|
+
|
|
274
302
|
def sphinx_index_name(suffix)
|
|
275
303
|
"#{self.class.source_of_sphinx_index.name.underscore.tr(':/\\', '_')}_#{suffix}"
|
|
276
304
|
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
module ThinkingSphinx
|
|
2
|
+
module ActiveRecord
|
|
3
|
+
module Scopes
|
|
4
|
+
def self.included(base)
|
|
5
|
+
base.class_eval do
|
|
6
|
+
extend ThinkingSphinx::ActiveRecord::Scopes::ClassMethods
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
module ClassMethods
|
|
11
|
+
def sphinx_scope(method, &block)
|
|
12
|
+
@sphinx_scopes ||= []
|
|
13
|
+
@sphinx_scopes << method
|
|
14
|
+
|
|
15
|
+
metaclass.instance_eval do
|
|
16
|
+
define_method(method) do |*args|
|
|
17
|
+
options = {:classes => classes_option}
|
|
18
|
+
options.merge! block.call(*args)
|
|
19
|
+
|
|
20
|
+
ThinkingSphinx::Search.new(options)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def sphinx_scopes
|
|
26
|
+
@sphinx_scopes || []
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def remove_sphinx_scopes
|
|
30
|
+
sphinx_scopes.each do |scope|
|
|
31
|
+
metaclass.send(:undef_method, scope)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
sphinx_scopes.clear
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -37,7 +37,8 @@ module ThinkingSphinx
|
|
|
37
37
|
end
|
|
38
38
|
|
|
39
39
|
def convert_nulls(clause, default = '')
|
|
40
|
-
default = "'#{default}'"
|
|
40
|
+
default = "'#{default}'" if default.is_a?(String)
|
|
41
|
+
default = 'NULL' if default.nil?
|
|
41
42
|
|
|
42
43
|
"COALESCE(#{clause}, #{default})"
|
|
43
44
|
end
|
|
@@ -132,4 +133,4 @@ module ThinkingSphinx
|
|
|
132
133
|
execute function, true
|
|
133
134
|
end
|
|
134
135
|
end
|
|
135
|
-
end
|
|
136
|
+
end
|
|
@@ -75,7 +75,9 @@ module ThinkingSphinx
|
|
|
75
75
|
@crc = options[:crc]
|
|
76
76
|
|
|
77
77
|
@type ||= :multi unless @query_source.nil?
|
|
78
|
-
|
|
78
|
+
if @type == :string && @crc
|
|
79
|
+
@type = is_many? ? :multi : :integer
|
|
80
|
+
end
|
|
79
81
|
|
|
80
82
|
source.attributes << self
|
|
81
83
|
end
|
|
@@ -89,14 +91,21 @@ module ThinkingSphinx
|
|
|
89
91
|
def to_select_sql
|
|
90
92
|
return nil unless include_as_association?
|
|
91
93
|
|
|
92
|
-
separator = all_ints? || @crc ? ',' : ' '
|
|
94
|
+
separator = all_ints? || all_datetimes? || @crc ? ',' : ' '
|
|
93
95
|
|
|
94
96
|
clause = @columns.collect { |column|
|
|
95
97
|
part = column_with_prefix(column)
|
|
96
|
-
type
|
|
98
|
+
case type
|
|
99
|
+
when :string
|
|
100
|
+
adapter.convert_nulls(part)
|
|
101
|
+
when :datetime
|
|
102
|
+
adapter.cast_to_datetime(part)
|
|
103
|
+
else
|
|
104
|
+
part
|
|
105
|
+
end
|
|
97
106
|
}.join(', ')
|
|
98
107
|
|
|
99
|
-
clause = adapter.cast_to_datetime(clause) if type == :datetime
|
|
108
|
+
# clause = adapter.cast_to_datetime(clause) if type == :datetime
|
|
100
109
|
clause = adapter.crc(clause) if @crc
|
|
101
110
|
clause = adapter.concatenate(clause, separator) if concat_ws?
|
|
102
111
|
clause = adapter.group_concatenate(clause, separator) if is_many?
|
|
@@ -125,10 +134,10 @@ module ThinkingSphinx
|
|
|
125
134
|
# Special case is the multi-valued attribute that needs some
|
|
126
135
|
# extra configuration.
|
|
127
136
|
#
|
|
128
|
-
def config_value(offset = nil)
|
|
137
|
+
def config_value(offset = nil, delta = false)
|
|
129
138
|
if type == :multi && ThinkingSphinx::Configuration.instance.type != "xml"
|
|
130
139
|
multi_config = include_as_association? ? "field" :
|
|
131
|
-
source_value(offset).gsub(/\s+/m, " ").strip
|
|
140
|
+
source_value(offset, delta).gsub(/\s+/m, " ").strip
|
|
132
141
|
"uint #{unique_name} from #{multi_config}"
|
|
133
142
|
else
|
|
134
143
|
unique_name
|
|
@@ -143,6 +152,8 @@ module ThinkingSphinx
|
|
|
143
152
|
def type
|
|
144
153
|
@type ||= begin
|
|
145
154
|
base_type = case
|
|
155
|
+
when is_many_datetimes?
|
|
156
|
+
:datetime
|
|
146
157
|
when is_many?, is_many_ints?
|
|
147
158
|
:multi
|
|
148
159
|
when @associations.values.flatten.length > 1
|
|
@@ -172,25 +183,29 @@ module ThinkingSphinx
|
|
|
172
183
|
end
|
|
173
184
|
|
|
174
185
|
def all_ints?
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
!column.nil? && column.type == :integer
|
|
181
|
-
}
|
|
182
|
-
}
|
|
186
|
+
all_of_type?(:integer)
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def all_datetimes?
|
|
190
|
+
all_of_type?(:datetime, :date, :timestamp)
|
|
183
191
|
end
|
|
184
192
|
|
|
185
193
|
private
|
|
186
194
|
|
|
187
|
-
def source_value(offset)
|
|
195
|
+
def source_value(offset, delta)
|
|
188
196
|
if is_string?
|
|
189
|
-
"#{query_source.to_s.dasherize}; #{columns.first.__name}"
|
|
190
|
-
|
|
191
|
-
|
|
197
|
+
return "#{query_source.to_s.dasherize}; #{columns.first.__name}"
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
query = query(offset)
|
|
201
|
+
|
|
202
|
+
if query_source == :ranged_query
|
|
203
|
+
query += query_clause
|
|
204
|
+
query += " AND #{query_delta.strip}" if delta
|
|
205
|
+
"ranged-query; #{query}; #{range_query}"
|
|
192
206
|
else
|
|
193
|
-
|
|
207
|
+
query += "WHERE #{query_delta.strip}" if delta
|
|
208
|
+
"query; #{query}"
|
|
194
209
|
end
|
|
195
210
|
end
|
|
196
211
|
|
|
@@ -212,6 +227,15 @@ FROM #{quote_table_name base_assoc.table} #{association_joins}
|
|
|
212
227
|
"WHERE #{foreign_key} >= $start AND #{foreign_key} <= $end"
|
|
213
228
|
end
|
|
214
229
|
|
|
230
|
+
def query_delta
|
|
231
|
+
foreign_key = foreign_key_for_mva base_association_for_mva
|
|
232
|
+
<<-SQL
|
|
233
|
+
#{foreign_key} IN (SELECT #{quote_column model.primary_key}
|
|
234
|
+
FROM #{model.quoted_table_name}
|
|
235
|
+
WHERE #{@source.index.delta_object.clause(model, true)})
|
|
236
|
+
SQL
|
|
237
|
+
end
|
|
238
|
+
|
|
215
239
|
def range_query
|
|
216
240
|
assoc = base_association_for_mva
|
|
217
241
|
foreign_key = foreign_key_for_mva assoc
|
|
@@ -259,14 +283,19 @@ FROM #{quote_table_name base_assoc.table} #{association_joins}
|
|
|
259
283
|
def is_many_ints?
|
|
260
284
|
concat_ws? && all_ints?
|
|
261
285
|
end
|
|
262
|
-
|
|
286
|
+
|
|
287
|
+
def is_many_datetimes?
|
|
288
|
+
is_many? && all_datetimes?
|
|
289
|
+
end
|
|
290
|
+
|
|
263
291
|
def type_from_database
|
|
264
292
|
klass = @associations.values.flatten.first ?
|
|
265
293
|
@associations.values.flatten.first.reflection.klass : @model
|
|
266
294
|
|
|
267
|
-
klass.columns.detect { |col|
|
|
295
|
+
column = klass.columns.detect { |col|
|
|
268
296
|
@columns.collect { |c| c.__name.to_s }.include? col.name
|
|
269
|
-
}
|
|
297
|
+
}
|
|
298
|
+
column.nil? ? nil : column.type
|
|
270
299
|
end
|
|
271
300
|
|
|
272
301
|
def translated_type_from_database
|
|
@@ -288,5 +317,16 @@ block:
|
|
|
288
317
|
MESSAGE
|
|
289
318
|
end
|
|
290
319
|
end
|
|
320
|
+
|
|
321
|
+
def all_of_type?(*column_types)
|
|
322
|
+
@columns.all? { |col|
|
|
323
|
+
klasses = @associations[col].empty? ? [@model] :
|
|
324
|
+
@associations[col].collect { |assoc| assoc.reflection.klass }
|
|
325
|
+
klasses.all? { |klass|
|
|
326
|
+
column = klass.columns.detect { |column| column.name == col.__name.to_s }
|
|
327
|
+
!column.nil? && column_types.include?(column.type)
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
end
|
|
291
331
|
end
|
|
292
332
|
end
|