ebeigarts-thinking-sphinx 1.1.22 → 1.2.10
Sign up to get free protection for your applications and to get access to all the features.
- 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
|