DrMark-thinking-sphinx 0.9.9 → 1.1.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 +64 -2
- data/lib/thinking_sphinx.rb +88 -11
- data/lib/thinking_sphinx/active_record.rb +136 -21
- data/lib/thinking_sphinx/active_record/delta.rb +43 -62
- data/lib/thinking_sphinx/active_record/has_many_association.rb +1 -1
- data/lib/thinking_sphinx/active_record/search.rb +7 -0
- data/lib/thinking_sphinx/adapters/abstract_adapter.rb +42 -0
- data/lib/thinking_sphinx/adapters/mysql_adapter.rb +54 -0
- data/lib/thinking_sphinx/adapters/postgresql_adapter.rb +130 -0
- data/lib/thinking_sphinx/association.rb +17 -0
- data/lib/thinking_sphinx/attribute.rb +171 -97
- data/lib/thinking_sphinx/collection.rb +126 -2
- data/lib/thinking_sphinx/configuration.rb +120 -171
- data/lib/thinking_sphinx/core/string.rb +15 -0
- data/lib/thinking_sphinx/deltas.rb +27 -0
- data/lib/thinking_sphinx/deltas/datetime_delta.rb +50 -0
- data/lib/thinking_sphinx/deltas/default_delta.rb +67 -0
- data/lib/thinking_sphinx/deltas/delayed_delta.rb +25 -0
- data/lib/thinking_sphinx/deltas/delayed_delta/delta_job.rb +24 -0
- data/lib/thinking_sphinx/deltas/delayed_delta/flag_as_deleted_job.rb +27 -0
- data/lib/thinking_sphinx/deltas/delayed_delta/job.rb +26 -0
- data/lib/thinking_sphinx/facet.rb +58 -0
- data/lib/thinking_sphinx/facet_collection.rb +60 -0
- data/lib/thinking_sphinx/field.rb +18 -52
- data/lib/thinking_sphinx/index.rb +246 -199
- data/lib/thinking_sphinx/index/builder.rb +85 -16
- data/lib/thinking_sphinx/rails_additions.rb +85 -5
- data/lib/thinking_sphinx/search.rb +459 -190
- data/lib/thinking_sphinx/tasks.rb +128 -0
- data/spec/unit/thinking_sphinx/active_record/delta_spec.rb +53 -124
- data/spec/unit/thinking_sphinx/active_record/has_many_association_spec.rb +2 -2
- data/spec/unit/thinking_sphinx/active_record_spec.rb +110 -30
- data/spec/unit/thinking_sphinx/attribute_spec.rb +16 -149
- data/spec/unit/thinking_sphinx/collection_spec.rb +14 -0
- data/spec/unit/thinking_sphinx/configuration_spec.rb +54 -412
- data/spec/unit/thinking_sphinx/core/string_spec.rb +9 -0
- data/spec/unit/thinking_sphinx/field_spec.rb +0 -79
- data/spec/unit/thinking_sphinx/index/builder_spec.rb +1 -29
- data/spec/unit/thinking_sphinx/index/faux_column_spec.rb +1 -39
- data/spec/unit/thinking_sphinx/index_spec.rb +78 -226
- data/spec/unit/thinking_sphinx/search_spec.rb +29 -228
- data/spec/unit/thinking_sphinx_spec.rb +23 -19
- data/tasks/distribution.rb +48 -0
- data/tasks/rails.rake +1 -0
- data/tasks/testing.rb +86 -0
- data/vendor/after_commit/LICENSE +20 -0
- data/vendor/after_commit/README +16 -0
- data/vendor/after_commit/Rakefile +22 -0
- data/vendor/after_commit/init.rb +8 -0
- data/vendor/after_commit/lib/after_commit.rb +45 -0
- data/vendor/after_commit/lib/after_commit/active_record.rb +114 -0
- data/vendor/after_commit/lib/after_commit/connection_adapters.rb +103 -0
- data/vendor/after_commit/test/after_commit_test.rb +53 -0
- data/vendor/delayed_job/lib/delayed/job.rb +251 -0
- data/vendor/delayed_job/lib/delayed/message_sending.rb +7 -0
- data/vendor/delayed_job/lib/delayed/performable_method.rb +55 -0
- data/vendor/delayed_job/lib/delayed/worker.rb +54 -0
- data/{lib → vendor/riddle/lib}/riddle.rb +9 -5
- data/{lib → vendor/riddle/lib}/riddle/client.rb +6 -26
- data/{lib → vendor/riddle/lib}/riddle/client/filter.rb +10 -1
- data/{lib → vendor/riddle/lib}/riddle/client/message.rb +0 -0
- data/{lib → vendor/riddle/lib}/riddle/client/response.rb +0 -0
- data/vendor/riddle/lib/riddle/configuration.rb +33 -0
- data/vendor/riddle/lib/riddle/configuration/distributed_index.rb +48 -0
- data/vendor/riddle/lib/riddle/configuration/index.rb +142 -0
- data/vendor/riddle/lib/riddle/configuration/indexer.rb +19 -0
- data/vendor/riddle/lib/riddle/configuration/remote_index.rb +17 -0
- data/vendor/riddle/lib/riddle/configuration/searchd.rb +25 -0
- data/vendor/riddle/lib/riddle/configuration/section.rb +37 -0
- data/vendor/riddle/lib/riddle/configuration/source.rb +23 -0
- data/vendor/riddle/lib/riddle/configuration/sql_source.rb +34 -0
- data/vendor/riddle/lib/riddle/configuration/xml_source.rb +28 -0
- data/vendor/riddle/lib/riddle/controller.rb +44 -0
- metadata +63 -10
- data/lib/test.rb +0 -46
- data/tasks/thinking_sphinx_tasks.rake +0 -1
- data/tasks/thinking_sphinx_tasks.rb +0 -86
@@ -9,7 +9,8 @@ module ThinkingSphinx
|
|
9
9
|
# Enjoy.
|
10
10
|
#
|
11
11
|
class Index
|
12
|
-
attr_accessor :model, :fields, :attributes, :conditions, :
|
12
|
+
attr_accessor :model, :fields, :attributes, :conditions, :groupings,
|
13
|
+
:delta_object, :options
|
13
14
|
|
14
15
|
# Create a new index instance by passing in the model it is tied to, and
|
15
16
|
# a block to build it with (optional but recommended). For documentation
|
@@ -31,8 +32,9 @@ module ThinkingSphinx
|
|
31
32
|
@fields = []
|
32
33
|
@attributes = []
|
33
34
|
@conditions = []
|
35
|
+
@groupings = []
|
34
36
|
@options = {}
|
35
|
-
@
|
37
|
+
@delta_object = nil
|
36
38
|
|
37
39
|
initialize_from_builder(&block) if block_given?
|
38
40
|
end
|
@@ -45,65 +47,36 @@ module ThinkingSphinx
|
|
45
47
|
model.name.underscore.tr(':/\\', '_')
|
46
48
|
end
|
47
49
|
|
48
|
-
def
|
49
|
-
|
50
|
-
|
50
|
+
def to_riddle_for_core(offset, index)
|
51
|
+
add_internal_attributes_and_facets
|
52
|
+
link!
|
53
|
+
|
54
|
+
source = Riddle::Configuration::SQLSource.new(
|
55
|
+
"#{name}_core_#{index}", adapter.sphinx_identifier
|
56
|
+
)
|
57
|
+
|
58
|
+
set_source_database_settings source
|
59
|
+
set_source_attributes source, offset
|
60
|
+
set_source_sql source, offset
|
61
|
+
set_source_settings source
|
62
|
+
|
63
|
+
source
|
51
64
|
end
|
52
65
|
|
53
|
-
def
|
54
|
-
|
55
|
-
add_internal_attributes
|
66
|
+
def to_riddle_for_delta(offset, index)
|
67
|
+
add_internal_attributes_and_facets
|
56
68
|
link!
|
57
69
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
db_adapter = case adapter
|
63
|
-
when :postgres
|
64
|
-
"pgsql"
|
65
|
-
when :mysql
|
66
|
-
"mysql"
|
67
|
-
else
|
68
|
-
raise "Unsupported Database Adapter: Sphinx only supports MySQL and PosgreSQL"
|
69
|
-
end
|
70
|
+
source = Riddle::Configuration::SQLSource.new(
|
71
|
+
"#{name}_delta_#{index}", adapter.sphinx_identifier
|
72
|
+
)
|
73
|
+
source.parent = "#{name}_core_#{index}"
|
70
74
|
|
71
|
-
|
72
|
-
|
73
|
-
source
|
74
|
-
{
|
75
|
-
type = #{db_adapter}
|
76
|
-
sql_host = #{database_conf[:host] || "localhost"}
|
77
|
-
sql_user = #{database_conf[:username] || database_conf[:user]}
|
78
|
-
sql_pass = #{(database_conf[:password] || "").gsub('#', '\#')}
|
79
|
-
sql_db = #{database_conf[:database]}
|
80
|
-
#{"sql_sock = #{database_conf[:socket]}" unless database_conf[:socket].blank? }
|
81
|
-
|
82
|
-
sql_query_pre = #{charset_type == "utf-8" && adapter == :mysql ? "SET NAMES utf8" : ""}
|
83
|
-
#{"sql_query_pre = SET SESSION group_concat_max_len = #{@options[:group_concat_max_len]}" if @options[:group_concat_max_len]}
|
84
|
-
sql_query_pre = #{to_sql_query_pre}
|
85
|
-
sql_query = #{to_sql(:offset => offset).gsub(/\n/, ' ')}
|
86
|
-
sql_query_range = #{to_sql_query_range :offset => offset}
|
87
|
-
sql_query_info = #{to_sql_query_info(offset)}
|
88
|
-
#{attr_sources}
|
89
|
-
}
|
90
|
-
SOURCE
|
91
|
-
|
92
|
-
if delta?
|
93
|
-
config += <<-SOURCE
|
94
|
-
|
95
|
-
source #{self.class.name(model)}_#{index}_delta : #{self.class.name(model)}_#{index}_core
|
96
|
-
{
|
97
|
-
sql_query_pre =
|
98
|
-
sql_query_pre = #{charset_type == "utf-8" && adapter == :mysql ? "SET NAMES utf8" : ""}
|
99
|
-
#{"sql_query_pre = SET SESSION group_concat_max_len = #{@options[:group_concat_max_len]}" if @options[:group_concat_max_len]}
|
100
|
-
sql_query = #{to_sql(:delta => true, :offset => offset).gsub(/\n/, ' ')}
|
101
|
-
sql_query_range = #{to_sql_query_range :offset => offset, :delta => true}
|
102
|
-
}
|
103
|
-
SOURCE
|
104
|
-
end
|
75
|
+
set_source_database_settings source
|
76
|
+
set_source_attributes source, offset
|
77
|
+
set_source_sql source, offset, true
|
105
78
|
|
106
|
-
|
79
|
+
source
|
107
80
|
end
|
108
81
|
|
109
82
|
# Link all the fields and associations to their corresponding
|
@@ -133,111 +106,16 @@ sql_query_range = #{to_sql_query_range :offset => offset, :delta => true}
|
|
133
106
|
}
|
134
107
|
end
|
135
108
|
|
136
|
-
# Generates the big SQL statement to get the data back for all the fields
|
137
|
-
# and attributes, using all the relevant association joins. If you want
|
138
|
-
# the version filtered for delta values, send through :delta => true in the
|
139
|
-
# options. Won't do much though if the index isn't set up to support a
|
140
|
-
# delta sibling.
|
141
|
-
#
|
142
|
-
# Examples:
|
143
|
-
#
|
144
|
-
# index.to_sql
|
145
|
-
# index.to_sql(:delta => true)
|
146
|
-
#
|
147
|
-
def to_sql(options={})
|
148
|
-
assocs = all_associations
|
149
|
-
|
150
|
-
where_clause = ""
|
151
|
-
if self.delta?
|
152
|
-
where_clause << " AND #{@model.quoted_table_name}.#{quote_column('delta')}" +" = #{options[:delta] ? db_boolean(true) : db_boolean(false)}"
|
153
|
-
end
|
154
|
-
unless @conditions.empty?
|
155
|
-
where_clause << " AND " << @conditions.join(" AND ")
|
156
|
-
end
|
157
|
-
|
158
|
-
unique_id_expr = "* #{ThinkingSphinx.indexed_models.size} + #{options[:offset] || 0}"
|
159
|
-
|
160
|
-
sql = <<-SQL
|
161
|
-
SELECT #{ (
|
162
|
-
["#{@model.quoted_table_name}.#{quote_column(@model.primary_key)} #{unique_id_expr} AS #{quote_column(@model.primary_key)} "] +
|
163
|
-
@fields.collect { |field| field.to_select_sql } +
|
164
|
-
@attributes.collect { |attribute| attribute.to_select_sql }
|
165
|
-
).join(", ") }
|
166
|
-
FROM #{ @model.table_name }
|
167
|
-
#{ assocs.collect { |assoc| assoc.to_sql }.join(' ') }
|
168
|
-
WHERE #{@model.quoted_table_name}.#{quote_column(@model.primary_key)} #{unique_id_expr} >= $start
|
169
|
-
AND #{@model.quoted_table_name}.#{quote_column(@model.primary_key)} #{unique_id_expr} <= $end
|
170
|
-
#{ where_clause }
|
171
|
-
GROUP BY #{ (
|
172
|
-
["#{@model.quoted_table_name}.#{quote_column(@model.primary_key)}"] +
|
173
|
-
@fields.collect { |field| field.to_group_sql }.compact +
|
174
|
-
@attributes.collect { |attribute| attribute.to_group_sql }.compact
|
175
|
-
).join(", ") }
|
176
|
-
SQL
|
177
|
-
|
178
|
-
if @model.connection.class.name == "ActiveRecord::ConnectionAdapters::MysqlAdapter"
|
179
|
-
sql += " ORDER BY NULL"
|
180
|
-
end
|
181
|
-
|
182
|
-
sql
|
183
|
-
end
|
184
|
-
|
185
|
-
# Simple helper method for the query info SQL - which is a statement that
|
186
|
-
# returns the single row for a corresponding id.
|
187
|
-
#
|
188
|
-
def to_sql_query_info(offset)
|
189
|
-
"SELECT * FROM #{@model.quoted_table_name} WHERE " +
|
190
|
-
" #{quote_column(@model.primary_key)} = (($id - #{offset}) / #{ThinkingSphinx.indexed_models.size})"
|
191
|
-
end
|
192
|
-
|
193
|
-
# Simple helper method for the query range SQL - which is a statement that
|
194
|
-
# returns minimum and maximum id values. These can be filtered by delta -
|
195
|
-
# so pass in :delta => true to get the delta version of the SQL.
|
196
|
-
#
|
197
|
-
def to_sql_query_range(options={})
|
198
|
-
unique_id_expr = "* #{ThinkingSphinx.indexed_models.size} + #{options[:offset] || 0}"
|
199
|
-
|
200
|
-
min_statement = "MIN(#{quote_column(@model.primary_key)} #{unique_id_expr})"
|
201
|
-
max_statement = "MAX(#{quote_column(@model.primary_key)} #{unique_id_expr})"
|
202
|
-
|
203
|
-
# Fix to handle Sphinx PostgreSQL bug (it doesn't like NULLs or 0's)
|
204
|
-
if adapter == :postgres
|
205
|
-
min_statement = "COALESCE(#{min_statement}, 1)"
|
206
|
-
max_statement = "COALESCE(#{max_statement}, 1)"
|
207
|
-
end
|
208
|
-
|
209
|
-
sql = "SELECT #{min_statement}, #{max_statement} " +
|
210
|
-
"FROM #{@model.quoted_table_name} "
|
211
|
-
sql << "WHERE #{@model.quoted_table_name}.#{quote_column('delta')} " +
|
212
|
-
"= #{options[:delta] ? db_boolean(true) : db_boolean(false)}" if self.delta?
|
213
|
-
sql
|
214
|
-
end
|
215
|
-
|
216
|
-
# Returns the SQL query to run before a full index - ie: nothing unless the
|
217
|
-
# index has a delta, and then it's an update statement to set delta values
|
218
|
-
# back to 0.
|
219
|
-
#
|
220
|
-
def to_sql_query_pre
|
221
|
-
self.delta? ? "UPDATE #{@model.quoted_table_name} SET #{quote_column('delta')} = #{db_boolean(false)}" : ""
|
222
|
-
end
|
223
|
-
|
224
109
|
# Flag to indicate whether this index has a corresponding delta index.
|
225
110
|
#
|
226
111
|
def delta?
|
227
|
-
|
112
|
+
!@delta_object.nil?
|
228
113
|
end
|
229
114
|
|
230
115
|
def adapter
|
231
|
-
@adapter ||=
|
232
|
-
when "ActiveRecord::ConnectionAdapters::MysqlAdapter"
|
233
|
-
:mysql
|
234
|
-
when "ActiveRecord::ConnectionAdapters::PostgreSQLAdapter"
|
235
|
-
:postgres
|
236
|
-
else
|
237
|
-
raise "Invalid Database Adapter: Sphinx only supports MySQL and PostgreSQL"
|
238
|
-
end
|
116
|
+
@adapter ||= @model.sphinx_database_adapter
|
239
117
|
end
|
240
|
-
|
118
|
+
|
241
119
|
def prefix_fields
|
242
120
|
@fields.select { |field| field.prefixes }
|
243
121
|
end
|
@@ -245,11 +123,23 @@ GROUP BY #{ (
|
|
245
123
|
def infix_fields
|
246
124
|
@fields.select { |field| field.infixes }
|
247
125
|
end
|
126
|
+
|
127
|
+
def index_options
|
128
|
+
all_index_options = ThinkingSphinx::Configuration.instance.index_options.clone
|
129
|
+
@options.keys.select { |key|
|
130
|
+
ThinkingSphinx::Configuration::IndexOptions.include?(key.to_s)
|
131
|
+
}.each { |key| all_index_options[key.to_sym] = @options[key] }
|
132
|
+
all_index_options
|
133
|
+
end
|
134
|
+
|
135
|
+
def quote_column(column)
|
136
|
+
@model.connection.quote_column_name(column)
|
137
|
+
end
|
248
138
|
|
249
139
|
private
|
250
140
|
|
251
|
-
def
|
252
|
-
|
141
|
+
def utf8?
|
142
|
+
self.index_options[:charset_type] == "utf-8"
|
253
143
|
end
|
254
144
|
|
255
145
|
# Does all the magic with the block provided to the base #initialize.
|
@@ -269,12 +159,33 @@ GROUP BY #{ (
|
|
269
159
|
stored_class = @model.store_full_sti_class ? @model.name : @model.name.demodulize
|
270
160
|
builder.where("#{@model.quoted_table_name}.#{quote_column(@model.inheritance_column)} = '#{stored_class}'")
|
271
161
|
end
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
@
|
276
|
-
@
|
277
|
-
@
|
162
|
+
|
163
|
+
set_model = Proc.new { |item| item.model = @model }
|
164
|
+
|
165
|
+
@fields = builder.fields &set_model
|
166
|
+
@attributes = builder.attributes.each &set_model
|
167
|
+
@conditions = builder.conditions
|
168
|
+
@groupings = builder.groupings
|
169
|
+
@delta_object = ThinkingSphinx::Deltas.parse self, builder.properties
|
170
|
+
@options = builder.properties
|
171
|
+
|
172
|
+
is_faceted = Proc.new { |item| item.faceted }
|
173
|
+
add_facet = Proc.new { |item| @model.sphinx_facets << item.to_facet }
|
174
|
+
|
175
|
+
@model.sphinx_facets ||= []
|
176
|
+
@fields.select( &is_faceted).each &add_facet
|
177
|
+
@attributes.select(&is_faceted).each &add_facet
|
178
|
+
|
179
|
+
# We want to make sure that if the database doesn't exist, then Thinking
|
180
|
+
# Sphinx doesn't mind when running non-TS tasks (like db:create, db:drop
|
181
|
+
# and db:migrate). It's a bit hacky, but I can't think of a better way.
|
182
|
+
rescue StandardError => err
|
183
|
+
case err.class.name
|
184
|
+
when "Mysql::Error", "Java::JavaSql::SQLException", "ActiveRecord::StatementInvalid"
|
185
|
+
return
|
186
|
+
else
|
187
|
+
raise err
|
188
|
+
end
|
278
189
|
end
|
279
190
|
|
280
191
|
# Returns all associations used amongst all the fields and attributes.
|
@@ -289,8 +200,8 @@ GROUP BY #{ (
|
|
289
200
|
}.flatten +
|
290
201
|
# attribute associations
|
291
202
|
@attributes.collect { |attrib|
|
292
|
-
attrib.associations.values
|
293
|
-
}.flatten
|
203
|
+
attrib.associations.values if attrib.include_as_association?
|
204
|
+
}.compact.flatten
|
294
205
|
).uniq.collect { |assoc|
|
295
206
|
# get ancestors as well as column-level associations
|
296
207
|
assoc.ancestors
|
@@ -324,53 +235,189 @@ GROUP BY #{ (
|
|
324
235
|
@associations[key] ||= Association.children(@model, key)
|
325
236
|
end
|
326
237
|
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
238
|
+
def crc_column
|
239
|
+
if @model.column_names.include?(@model.inheritance_column)
|
240
|
+
adapter.cast_to_unsigned(adapter.convert_nulls(
|
241
|
+
adapter.crc(adapter.quote_with_table(@model.inheritance_column), true),
|
242
|
+
@model.to_crc32
|
243
|
+
))
|
333
244
|
else
|
334
|
-
|
245
|
+
@model.to_crc32.to_s
|
335
246
|
end
|
336
247
|
end
|
337
248
|
|
338
|
-
def
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
249
|
+
def add_internal_attributes_and_facets
|
250
|
+
add_internal_attribute :sphinx_internal_id, :integer, @model.primary_key.to_sym
|
251
|
+
add_internal_attribute :class_crc, :integer, crc_column, true
|
252
|
+
add_internal_attribute :subclass_crcs, :multi, subclasses_to_s
|
253
|
+
add_internal_attribute :sphinx_deleted, :integer, "0"
|
254
|
+
|
255
|
+
add_internal_facet :class_crc
|
256
|
+
end
|
257
|
+
|
258
|
+
def add_internal_attribute(name, type, contents, facet = false)
|
259
|
+
return unless attribute_by_alias(name).nil?
|
260
|
+
|
261
|
+
@attributes << Attribute.new(
|
262
|
+
FauxColumn.new(contents),
|
263
|
+
:type => type,
|
264
|
+
:as => name,
|
265
|
+
:facet => facet
|
266
|
+
)
|
267
|
+
end
|
268
|
+
|
269
|
+
def add_internal_facet(name)
|
270
|
+
return unless facet_by_alias(name).nil?
|
271
|
+
|
272
|
+
@model.sphinx_facets << ClassFacet.new(attribute_by_alias(name))
|
273
|
+
end
|
274
|
+
|
275
|
+
def attribute_by_alias(attr_alias)
|
276
|
+
@attributes.detect { |attrib| attrib.alias == attr_alias }
|
277
|
+
end
|
278
|
+
|
279
|
+
def facet_by_alias(name)
|
280
|
+
@model.sphinx_facets.detect { |facet| facet.name == name }
|
281
|
+
end
|
282
|
+
|
283
|
+
def subclasses_to_s
|
284
|
+
"'" + (@model.send(:subclasses).collect { |klass|
|
285
|
+
klass.to_crc32.to_s
|
286
|
+
} << @model.to_crc32.to_s).join(",") + "'"
|
287
|
+
end
|
288
|
+
|
289
|
+
def set_source_database_settings(source)
|
290
|
+
config = @model.connection.instance_variable_get(:@config)
|
291
|
+
|
292
|
+
source.sql_host = config[:host] || "localhost"
|
293
|
+
source.sql_user = config[:username] || config[:user] || ""
|
294
|
+
source.sql_pass = (config[:password].to_s || "").gsub('#', '\#')
|
295
|
+
source.sql_db = config[:database]
|
296
|
+
source.sql_port = config[:port]
|
297
|
+
source.sql_sock = config[:socket]
|
298
|
+
end
|
299
|
+
|
300
|
+
def set_source_attributes(source, offset = nil)
|
301
|
+
attributes.each do |attrib|
|
302
|
+
source.send(attrib.type_to_config) << attrib.config_value(offset)
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
def set_source_sql(source, offset, delta = false)
|
307
|
+
source.sql_query = to_sql(:offset => offset, :delta => delta).gsub(/\n/, ' ')
|
308
|
+
source.sql_query_range = to_sql_query_range(:delta => delta)
|
309
|
+
source.sql_query_info = to_sql_query_info(offset)
|
310
|
+
|
311
|
+
source.sql_query_pre += send(!delta ? :sql_query_pre_for_core : :sql_query_pre_for_delta)
|
312
|
+
|
313
|
+
if @options[:group_concat_max_len]
|
314
|
+
source.sql_query_pre << "SET SESSION group_concat_max_len = #{@options[:group_concat_max_len]}"
|
315
|
+
end
|
316
|
+
|
317
|
+
source.sql_query_pre += [adapter.utf8_query_pre].compact if utf8?
|
318
|
+
end
|
319
|
+
|
320
|
+
def set_source_settings(source)
|
321
|
+
ThinkingSphinx::Configuration.instance.source_options.each do |key, value|
|
322
|
+
source.send("#{key}=".to_sym, value)
|
323
|
+
end
|
324
|
+
|
325
|
+
@options.each do |key, value|
|
326
|
+
source.send("#{key}=".to_sym, value) if ThinkingSphinx::Configuration::SourceOptions.include?(key.to_s) && !value.nil?
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
def sql_query_pre_for_core
|
331
|
+
if self.delta? && !@delta_object.reset_query(@model).blank?
|
332
|
+
[@delta_object.reset_query(@model)]
|
343
333
|
else
|
344
|
-
|
334
|
+
[]
|
345
335
|
end
|
346
336
|
end
|
347
337
|
|
348
|
-
def
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
338
|
+
def sql_query_pre_for_delta
|
339
|
+
[""]
|
340
|
+
end
|
341
|
+
|
342
|
+
# Generates the big SQL statement to get the data back for all the fields
|
343
|
+
# and attributes, using all the relevant association joins. If you want
|
344
|
+
# the version filtered for delta values, send through :delta => true in the
|
345
|
+
# options. Won't do much though if the index isn't set up to support a
|
346
|
+
# delta sibling.
|
347
|
+
#
|
348
|
+
# Examples:
|
349
|
+
#
|
350
|
+
# index.to_sql
|
351
|
+
# index.to_sql(:delta => true)
|
352
|
+
#
|
353
|
+
def to_sql(options={})
|
354
|
+
assocs = all_associations
|
354
355
|
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
356
|
+
where_clause = ""
|
357
|
+
if self.delta? && !@delta_object.clause(@model, options[:delta]).blank?
|
358
|
+
where_clause << " AND #{@delta_object.clause(@model, options[:delta])}"
|
359
|
+
end
|
360
|
+
unless @conditions.empty?
|
361
|
+
where_clause << " AND " << @conditions.join(" AND ")
|
362
|
+
end
|
360
363
|
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
:type => :multi,
|
366
|
-
:as => :subclass_crcs
|
367
|
-
) unless @attributes.detect { |attr| attr.alias == :subclass_crcs }
|
364
|
+
internal_groupings = []
|
365
|
+
if @model.column_names.include?(@model.inheritance_column)
|
366
|
+
internal_groupings << "#{@model.quoted_table_name}.#{quote_column(@model.inheritance_column)}"
|
367
|
+
end
|
368
368
|
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
369
|
+
unique_id_expr = ThinkingSphinx.unique_id_expression(options[:offset])
|
370
|
+
|
371
|
+
sql = <<-SQL
|
372
|
+
SELECT #{ (
|
373
|
+
["#{@model.quoted_table_name}.#{quote_column(@model.primary_key)} #{unique_id_expr} AS #{quote_column(@model.primary_key)} "] +
|
374
|
+
@fields.collect { |field| field.to_select_sql } +
|
375
|
+
@attributes.collect { |attribute| attribute.to_select_sql }
|
376
|
+
).compact.join(", ") }
|
377
|
+
FROM #{ @model.table_name }
|
378
|
+
#{ assocs.collect { |assoc| assoc.to_sql }.join(' ') }
|
379
|
+
WHERE #{@model.quoted_table_name}.#{quote_column(@model.primary_key)} >= $start
|
380
|
+
AND #{@model.quoted_table_name}.#{quote_column(@model.primary_key)} <= $end
|
381
|
+
#{ where_clause }
|
382
|
+
GROUP BY #{ (
|
383
|
+
["#{@model.quoted_table_name}.#{quote_column(@model.primary_key)}"] +
|
384
|
+
@fields.collect { |field| field.to_group_sql }.compact +
|
385
|
+
@attributes.collect { |attribute| attribute.to_group_sql }.compact +
|
386
|
+
@groupings + internal_groupings
|
387
|
+
).join(", ") }
|
388
|
+
SQL
|
389
|
+
|
390
|
+
sql += " ORDER BY NULL" if adapter.sphinx_identifier == "mysql"
|
391
|
+
sql
|
392
|
+
end
|
393
|
+
|
394
|
+
# Simple helper method for the query info SQL - which is a statement that
|
395
|
+
# returns the single row for a corresponding id.
|
396
|
+
#
|
397
|
+
def to_sql_query_info(offset)
|
398
|
+
"SELECT * FROM #{@model.quoted_table_name} WHERE " +
|
399
|
+
" #{quote_column(@model.primary_key)} = (($id - #{offset}) / #{ThinkingSphinx.indexed_models.size})"
|
400
|
+
end
|
401
|
+
|
402
|
+
# Simple helper method for the query range SQL - which is a statement that
|
403
|
+
# returns minimum and maximum id values. These can be filtered by delta -
|
404
|
+
# so pass in :delta => true to get the delta version of the SQL.
|
405
|
+
#
|
406
|
+
def to_sql_query_range(options={})
|
407
|
+
min_statement = adapter.convert_nulls(
|
408
|
+
"MIN(#{quote_column(@model.primary_key)})", 1
|
409
|
+
)
|
410
|
+
max_statement = adapter.convert_nulls(
|
411
|
+
"MAX(#{quote_column(@model.primary_key)})", 1
|
412
|
+
)
|
413
|
+
|
414
|
+
sql = "SELECT #{min_statement}, #{max_statement} " +
|
415
|
+
"FROM #{@model.quoted_table_name} "
|
416
|
+
if self.delta? && !@delta_object.clause(@model, options[:delta]).blank?
|
417
|
+
sql << "WHERE #{@delta_object.clause(@model, options[:delta])}"
|
418
|
+
end
|
419
|
+
|
420
|
+
sql
|
374
421
|
end
|
375
422
|
end
|
376
423
|
end
|