freelancing-god-thinking-sphinx 1.1.2 → 1.1.3
Sign up to get free protection for your applications and to get access to all the features.
- data/README +4 -0
- data/lib/thinking_sphinx.rb +4 -2
- data/lib/thinking_sphinx/active_record.rb +8 -10
- data/lib/thinking_sphinx/active_record/search.rb +7 -0
- data/lib/thinking_sphinx/adapters/abstract_adapter.rb +26 -20
- data/lib/thinking_sphinx/adapters/mysql_adapter.rb +48 -4
- data/lib/thinking_sphinx/adapters/postgresql_adapter.rb +117 -71
- data/lib/thinking_sphinx/attribute.rb +27 -82
- data/lib/thinking_sphinx/collection.rb +34 -19
- data/lib/thinking_sphinx/configuration.rb +0 -25
- data/lib/thinking_sphinx/core/string.rb +22 -0
- data/lib/thinking_sphinx/deltas/datetime_delta.rb +1 -12
- data/lib/thinking_sphinx/deltas/default_delta.rb +9 -3
- data/lib/thinking_sphinx/field.rb +18 -54
- data/lib/thinking_sphinx/index.rb +105 -145
- data/lib/thinking_sphinx/index/builder.rb +2 -2
- data/lib/thinking_sphinx/search.rb +17 -0
- data/spec/unit/thinking_sphinx/active_record_spec.rb +2 -1
- data/spec/unit/thinking_sphinx/core/string_spec.rb +9 -0
- data/spec/unit/thinking_sphinx/index_spec.rb +2 -2
- data/tasks/distribution.rb +48 -0
- data/tasks/testing.rb +86 -0
- metadata +7 -2
- data/tasks/thinking_sphinx_tasks.rake +0 -1
- data/tasks/thinking_sphinx_tasks.rb +0 -128
@@ -33,22 +33,27 @@ module ThinkingSphinx
|
|
33
33
|
end
|
34
34
|
|
35
35
|
def self.instances_from_matches(matches, options = {})
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
36
|
+
if klass = options[:class]
|
37
|
+
instances_from_class klass, matches, options
|
38
|
+
else
|
39
|
+
instances_from_classes matches, options
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.instances_from_class(klass, matches, options = {})
|
40
44
|
index_options = klass.sphinx_index_options
|
41
|
-
|
45
|
+
|
42
46
|
ids = matches.collect { |match| match[:attributes]["sphinx_internal_id"] }
|
43
47
|
instances = ids.length > 0 ? klass.find(
|
44
48
|
:all,
|
45
49
|
:conditions => {klass.primary_key.to_sym => ids},
|
46
50
|
:include => (options[:include] || index_options[:include]),
|
47
|
-
:select => (options[:select]
|
51
|
+
:select => (options[:select] || index_options[:select])
|
48
52
|
) : []
|
49
|
-
|
50
|
-
# Raise an exception if we find records in Sphinx but not in the DB, so
|
51
|
-
# can retry without them. See
|
53
|
+
|
54
|
+
# Raise an exception if we find records in Sphinx but not in the DB, so
|
55
|
+
# the search method can retry without them. See
|
56
|
+
# ThinkingSphinx::Search.retry_search_on_stale_index.
|
52
57
|
if options[:raise_on_stale] && instances.length < ids.length
|
53
58
|
stale_ids = ids - instances.map {|i| i.id }
|
54
59
|
raise StaleIdsException, stale_ids
|
@@ -59,14 +64,24 @@ module ThinkingSphinx
|
|
59
64
|
}
|
60
65
|
end
|
61
66
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
67
|
+
# Group results by class and call #find(:all) once for each group to reduce
|
68
|
+
# the number of #find's in multi-model searches.
|
69
|
+
#
|
70
|
+
def self.instances_from_classes(matches, options = {})
|
71
|
+
groups = matches.group_by { |match| match[:attributes]["class_crc"] }
|
72
|
+
groups.each do |crc, group|
|
73
|
+
group.replace(
|
74
|
+
instances_from_class(class_from_crc(crc), group, options)
|
75
|
+
)
|
76
|
+
end
|
77
|
+
|
78
|
+
matches.collect do |match|
|
79
|
+
groups.detect { |crc, group|
|
80
|
+
crc == match[:attributes]["class_crc"]
|
81
|
+
}[1].detect { |obj|
|
82
|
+
obj.id == match[:attributes]["sphinx_internal_id"]
|
83
|
+
}
|
84
|
+
end
|
70
85
|
end
|
71
86
|
|
72
87
|
def self.class_from_crc(crc)
|
@@ -98,9 +113,9 @@ module ThinkingSphinx
|
|
98
113
|
each_with_attribute method.to_s.gsub(/^each_with_/, ''), &block
|
99
114
|
end
|
100
115
|
|
101
|
-
def
|
116
|
+
def each_with_groupby_and_count(&block)
|
102
117
|
results[:matches].each_with_index do |match, index|
|
103
|
-
yield self[index], match[:attributes]["@
|
118
|
+
yield self[index], match[:attributes]["@groupby"], match[:attributes]["@count"]
|
104
119
|
end
|
105
120
|
end
|
106
121
|
|
@@ -156,31 +156,6 @@ module ThinkingSphinx
|
|
156
156
|
end
|
157
157
|
end
|
158
158
|
|
159
|
-
def hash_to_config(hash)
|
160
|
-
hash.collect { |key, value|
|
161
|
-
translated_value = case value
|
162
|
-
when TrueClass
|
163
|
-
"1"
|
164
|
-
when FalseClass
|
165
|
-
"0"
|
166
|
-
when NilClass, ""
|
167
|
-
next
|
168
|
-
else
|
169
|
-
value
|
170
|
-
end
|
171
|
-
" #{key} = #{translated_value}"
|
172
|
-
}.join("\n")
|
173
|
-
end
|
174
|
-
|
175
|
-
def self.options_merge(base, extra)
|
176
|
-
base = base.clone
|
177
|
-
extra.each do |key, value|
|
178
|
-
next if value.nil? || value == ""
|
179
|
-
base[key] = value
|
180
|
-
end
|
181
|
-
base
|
182
|
-
end
|
183
|
-
|
184
159
|
def address
|
185
160
|
@configuration.searchd.address
|
186
161
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module ThinkingSphinx
|
2
|
+
module Core
|
3
|
+
module String
|
4
|
+
|
5
|
+
def to_crc32
|
6
|
+
result = 0xFFFFFFFF
|
7
|
+
self.each_byte do |byte|
|
8
|
+
result ^= byte
|
9
|
+
8.times do
|
10
|
+
result = (result >> 1) ^ (0xEDB88320 * (result & 1))
|
11
|
+
end
|
12
|
+
end
|
13
|
+
result ^ 0xFFFFFFFF
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class String
|
21
|
+
include ThinkingSphinx::Core::String
|
22
|
+
end
|
@@ -40,22 +40,11 @@ module ThinkingSphinx
|
|
40
40
|
def clause(model, toggled)
|
41
41
|
if toggled
|
42
42
|
"#{model.quoted_table_name}.#{@index.quote_column(@column.to_s)}" +
|
43
|
-
" > #{time_difference}"
|
43
|
+
" > #{adapter.time_difference(@threshold)}"
|
44
44
|
else
|
45
45
|
nil
|
46
46
|
end
|
47
47
|
end
|
48
|
-
|
49
|
-
def time_difference
|
50
|
-
case @index.adapter
|
51
|
-
when :mysql
|
52
|
-
"DATE_SUB(NOW(), INTERVAL #{@threshold} SECOND)"
|
53
|
-
when :postgres
|
54
|
-
"current_timestamp - interval '#{@threshold} seconds'"
|
55
|
-
else
|
56
|
-
raise "Unknown Database Adapter"
|
57
|
-
end
|
58
|
-
end
|
59
48
|
end
|
60
49
|
end
|
61
50
|
end
|
@@ -37,12 +37,12 @@ module ThinkingSphinx
|
|
37
37
|
|
38
38
|
def reset_query(model)
|
39
39
|
"UPDATE #{model.quoted_table_name} SET " +
|
40
|
-
"#{@index.quote_column(@column.to_s)} = #{
|
40
|
+
"#{@index.quote_column(@column.to_s)} = #{adapter.boolean(false)}"
|
41
41
|
end
|
42
42
|
|
43
43
|
def clause(model, toggled)
|
44
44
|
"#{model.quoted_table_name}.#{@index.quote_column(@column.to_s)}" +
|
45
|
-
" = #{
|
45
|
+
" = #{adapter.boolean(toggled)}"
|
46
46
|
end
|
47
47
|
|
48
48
|
protected
|
@@ -53,7 +53,13 @@ module ThinkingSphinx
|
|
53
53
|
|
54
54
|
def delta_index_name(model)
|
55
55
|
"#{model.source_of_sphinx_index.name.underscore.tr(':/\\', '_')}_delta"
|
56
|
-
end
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def adapter
|
61
|
+
@adapter = @index.model.sphinx_database_adapter
|
62
|
+
end
|
57
63
|
end
|
58
64
|
end
|
59
65
|
end
|
@@ -8,7 +8,8 @@ module ThinkingSphinx
|
|
8
8
|
# associations. Which can get messy. Use Index.link!, it really helps.
|
9
9
|
#
|
10
10
|
class Field
|
11
|
-
attr_accessor :alias, :columns, :sortable, :associations, :model, :infixes,
|
11
|
+
attr_accessor :alias, :columns, :sortable, :associations, :model, :infixes,
|
12
|
+
:prefixes, :faceted
|
12
13
|
|
13
14
|
# To create a new field, you'll need to pass in either a single Column
|
14
15
|
# or an array of them, and some (optional) options. The columns are
|
@@ -58,10 +59,11 @@ module ThinkingSphinx
|
|
58
59
|
|
59
60
|
raise "Cannot define a field with no columns. Maybe you are trying to index a field with a reserved name (id, name). You can fix this error by using a symbol rather than a bare name (:id instead of id)." if @columns.empty? || @columns.any? { |column| !column.respond_to?(:__stack) }
|
60
61
|
|
61
|
-
@alias
|
62
|
-
@sortable
|
63
|
-
@infixes
|
64
|
-
@prefixes
|
62
|
+
@alias = options[:as]
|
63
|
+
@sortable = options[:sortable] || false
|
64
|
+
@infixes = options[:infixes] || false
|
65
|
+
@prefixes = options[:prefixes] || false
|
66
|
+
@faceted = options[:facet] || false
|
65
67
|
end
|
66
68
|
|
67
69
|
# Get the part of the SELECT clause related to this field. Don't forget
|
@@ -75,10 +77,10 @@ module ThinkingSphinx
|
|
75
77
|
column_with_prefix(column)
|
76
78
|
}.join(', ')
|
77
79
|
|
78
|
-
clause = concatenate(clause) if concat_ws?
|
79
|
-
clause = group_concatenate(clause) if is_many?
|
80
|
+
clause = adapter.concatenate(clause) if concat_ws?
|
81
|
+
clause = adapter.group_concatenate(clause) if is_many?
|
80
82
|
|
81
|
-
"#{cast_to_string clause } AS #{quote_column(unique_name)}"
|
83
|
+
"#{adapter.cast_to_string clause } AS #{quote_column(unique_name)}"
|
82
84
|
end
|
83
85
|
|
84
86
|
# Get the part of the GROUP BY clause related to this field - if one is
|
@@ -110,41 +112,16 @@ module ThinkingSphinx
|
|
110
112
|
end
|
111
113
|
end
|
112
114
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
when "ActiveRecord::ConnectionAdapters::MysqlAdapter"
|
118
|
-
"CONCAT_WS(' ', #{clause})"
|
119
|
-
when "ActiveRecord::ConnectionAdapters::PostgreSQLAdapter"
|
120
|
-
clause.split(', ').collect { |field|
|
121
|
-
"COALESCE(#{field}, '')"
|
122
|
-
}.join(" || ' ' || ")
|
123
|
-
else
|
124
|
-
clause
|
125
|
-
end
|
115
|
+
def to_facet
|
116
|
+
return nil unless @faceted
|
117
|
+
|
118
|
+
ThinkingSphinx::Facet.new(unique_name, @columns, self)
|
126
119
|
end
|
127
120
|
|
128
|
-
|
129
|
-
case @model.connection.class.name
|
130
|
-
when "ActiveRecord::ConnectionAdapters::MysqlAdapter"
|
131
|
-
"GROUP_CONCAT(#{clause} SEPARATOR ' ')"
|
132
|
-
when "ActiveRecord::ConnectionAdapters::PostgreSQLAdapter"
|
133
|
-
"array_to_string(array_accum(#{clause}), ' ')"
|
134
|
-
else
|
135
|
-
clause
|
136
|
-
end
|
137
|
-
end
|
121
|
+
private
|
138
122
|
|
139
|
-
def
|
140
|
-
|
141
|
-
when "ActiveRecord::ConnectionAdapters::MysqlAdapter"
|
142
|
-
"CAST(#{clause} AS CHAR)"
|
143
|
-
when "ActiveRecord::ConnectionAdapters::PostgreSQLAdapter"
|
144
|
-
clause
|
145
|
-
else
|
146
|
-
clause
|
147
|
-
end
|
123
|
+
def adapter
|
124
|
+
@adapter ||= @model.sphinx_database_adapter
|
148
125
|
end
|
149
126
|
|
150
127
|
def quote_column(column)
|
@@ -158,16 +135,7 @@ module ThinkingSphinx
|
|
158
135
|
def concat_ws?
|
159
136
|
@columns.length > 1 || multiple_associations?
|
160
137
|
end
|
161
|
-
|
162
|
-
# Checks the association tree for each column - if they're all the same,
|
163
|
-
# returns false.
|
164
|
-
#
|
165
|
-
def multiple_sources?
|
166
|
-
first = associations[@columns.first]
|
167
|
-
|
168
|
-
!@columns.all? { |col| associations[col] == first }
|
169
|
-
end
|
170
|
-
|
138
|
+
|
171
139
|
# Checks whether any column requires multiple associations (which only
|
172
140
|
# happens for polymorphic situations).
|
173
141
|
#
|
@@ -200,9 +168,5 @@ module ThinkingSphinx
|
|
200
168
|
def is_many?
|
201
169
|
associations.values.flatten.any? { |assoc| assoc.is_many? }
|
202
170
|
end
|
203
|
-
|
204
|
-
def is_string?
|
205
|
-
columns.all? { |col| col.is_string? }
|
206
|
-
end
|
207
171
|
end
|
208
172
|
end
|
@@ -46,18 +46,13 @@ module ThinkingSphinx
|
|
46
46
|
def self.name(model)
|
47
47
|
model.name.underscore.tr(':/\\', '_')
|
48
48
|
end
|
49
|
-
|
50
|
-
def to_riddle(model, index, offset)
|
51
|
-
add_internal_attributes
|
52
|
-
link!
|
53
|
-
end
|
54
49
|
|
55
50
|
def to_riddle_for_core(offset, index)
|
56
51
|
add_internal_attributes
|
57
52
|
link!
|
58
53
|
|
59
54
|
source = Riddle::Configuration::SQLSource.new(
|
60
|
-
"#{name}_core_#{index}",
|
55
|
+
"#{name}_core_#{index}", adapter.sphinx_identifier
|
61
56
|
)
|
62
57
|
|
63
58
|
set_source_database_settings source
|
@@ -73,7 +68,7 @@ module ThinkingSphinx
|
|
73
68
|
link!
|
74
69
|
|
75
70
|
source = Riddle::Configuration::SQLSource.new(
|
76
|
-
"#{name}_delta_#{index}",
|
71
|
+
"#{name}_delta_#{index}", adapter.sphinx_identifier
|
77
72
|
)
|
78
73
|
source.parent = "#{name}_core_#{index}"
|
79
74
|
|
@@ -111,92 +106,6 @@ module ThinkingSphinx
|
|
111
106
|
}
|
112
107
|
end
|
113
108
|
|
114
|
-
# Generates the big SQL statement to get the data back for all the fields
|
115
|
-
# and attributes, using all the relevant association joins. If you want
|
116
|
-
# the version filtered for delta values, send through :delta => true in the
|
117
|
-
# options. Won't do much though if the index isn't set up to support a
|
118
|
-
# delta sibling.
|
119
|
-
#
|
120
|
-
# Examples:
|
121
|
-
#
|
122
|
-
# index.to_sql
|
123
|
-
# index.to_sql(:delta => true)
|
124
|
-
#
|
125
|
-
def to_sql(options={})
|
126
|
-
assocs = all_associations
|
127
|
-
|
128
|
-
where_clause = ""
|
129
|
-
if self.delta? && !@delta_object.clause(@model, options[:delta]).blank?
|
130
|
-
where_clause << " AND #{@delta_object.clause(@model, options[:delta])}"
|
131
|
-
end
|
132
|
-
unless @conditions.empty?
|
133
|
-
where_clause << " AND " << @conditions.join(" AND ")
|
134
|
-
end
|
135
|
-
|
136
|
-
internal_groupings = []
|
137
|
-
if @model.column_names.include?(@model.inheritance_column)
|
138
|
-
internal_groupings << "#{@model.quoted_table_name}.#{quote_column(@model.inheritance_column)}"
|
139
|
-
end
|
140
|
-
|
141
|
-
unique_id_expr = "* #{ThinkingSphinx.indexed_models.size} + #{options[:offset] || 0}"
|
142
|
-
|
143
|
-
sql = <<-SQL
|
144
|
-
SELECT #{ (
|
145
|
-
["#{@model.quoted_table_name}.#{quote_column(@model.primary_key)} #{unique_id_expr} AS #{quote_column(@model.primary_key)} "] +
|
146
|
-
@fields.collect { |field| field.to_select_sql } +
|
147
|
-
@attributes.collect { |attribute| attribute.to_select_sql }
|
148
|
-
).join(", ") }
|
149
|
-
FROM #{ @model.table_name }
|
150
|
-
#{ assocs.collect { |assoc| assoc.to_sql }.join(' ') }
|
151
|
-
WHERE #{@model.quoted_table_name}.#{quote_column(@model.primary_key)} >= $start
|
152
|
-
AND #{@model.quoted_table_name}.#{quote_column(@model.primary_key)} <= $end
|
153
|
-
#{ where_clause }
|
154
|
-
GROUP BY #{ (
|
155
|
-
["#{@model.quoted_table_name}.#{quote_column(@model.primary_key)}"] +
|
156
|
-
@fields.collect { |field| field.to_group_sql }.compact +
|
157
|
-
@attributes.collect { |attribute| attribute.to_group_sql }.compact +
|
158
|
-
@groupings + internal_groupings
|
159
|
-
).join(", ") }
|
160
|
-
SQL
|
161
|
-
|
162
|
-
if @model.connection.class.name == "ActiveRecord::ConnectionAdapters::MysqlAdapter"
|
163
|
-
sql += " ORDER BY NULL"
|
164
|
-
end
|
165
|
-
|
166
|
-
sql
|
167
|
-
end
|
168
|
-
|
169
|
-
# Simple helper method for the query info SQL - which is a statement that
|
170
|
-
# returns the single row for a corresponding id.
|
171
|
-
#
|
172
|
-
def to_sql_query_info(offset)
|
173
|
-
"SELECT * FROM #{@model.quoted_table_name} WHERE " +
|
174
|
-
" #{quote_column(@model.primary_key)} = (($id - #{offset}) / #{ThinkingSphinx.indexed_models.size})"
|
175
|
-
end
|
176
|
-
|
177
|
-
# Simple helper method for the query range SQL - which is a statement that
|
178
|
-
# returns minimum and maximum id values. These can be filtered by delta -
|
179
|
-
# so pass in :delta => true to get the delta version of the SQL.
|
180
|
-
#
|
181
|
-
def to_sql_query_range(options={})
|
182
|
-
min_statement = "MIN(#{quote_column(@model.primary_key)})"
|
183
|
-
max_statement = "MAX(#{quote_column(@model.primary_key)})"
|
184
|
-
|
185
|
-
# Fix to handle Sphinx PostgreSQL bug (it doesn't like NULLs or 0's)
|
186
|
-
if adapter == :postgres
|
187
|
-
min_statement = "COALESCE(#{min_statement}, 1)"
|
188
|
-
max_statement = "COALESCE(#{max_statement}, 1)"
|
189
|
-
end
|
190
|
-
|
191
|
-
sql = "SELECT #{min_statement}, #{max_statement} " +
|
192
|
-
"FROM #{@model.quoted_table_name} "
|
193
|
-
if self.delta? && !@delta_object.clause(@model, options[:delta]).blank?
|
194
|
-
sql << "WHERE #{@delta_object.clause(@model, options[:delta])}"
|
195
|
-
end
|
196
|
-
|
197
|
-
sql
|
198
|
-
end
|
199
|
-
|
200
109
|
# Flag to indicate whether this index has a corresponding delta index.
|
201
110
|
#
|
202
111
|
def delta?
|
@@ -204,14 +113,7 @@ GROUP BY #{ (
|
|
204
113
|
end
|
205
114
|
|
206
115
|
def adapter
|
207
|
-
@adapter ||=
|
208
|
-
when "ActiveRecord::ConnectionAdapters::MysqlAdapter"
|
209
|
-
:mysql
|
210
|
-
when "ActiveRecord::ConnectionAdapters::PostgreSQLAdapter"
|
211
|
-
:postgres
|
212
|
-
else
|
213
|
-
raise "Invalid Database Adapter: Sphinx only supports MySQL and PostgreSQL"
|
214
|
-
end
|
116
|
+
@adapter ||= @model.sphinx_database_adapter
|
215
117
|
end
|
216
118
|
|
217
119
|
def prefix_fields
|
@@ -229,30 +131,11 @@ GROUP BY #{ (
|
|
229
131
|
}.each { |key| all_index_options[key.to_sym] = @options[key] }
|
230
132
|
all_index_options
|
231
133
|
end
|
232
|
-
|
233
|
-
def source_options
|
234
|
-
all_source_options = ThinkingSphinx::Configuration.instance.source_options.clone
|
235
|
-
@options.keys.select { |key|
|
236
|
-
ThinkingSphinx::Configuration::SourceOptions.include?(key.to_s)
|
237
|
-
}.each { |key| all_source_options[key.to_sym] = @options[key] }
|
238
|
-
all_source_options
|
239
|
-
end
|
240
|
-
|
134
|
+
|
241
135
|
def quote_column(column)
|
242
136
|
@model.connection.quote_column_name(column)
|
243
137
|
end
|
244
138
|
|
245
|
-
# Returns the proper boolean value string literal for the
|
246
|
-
# current database adapter.
|
247
|
-
#
|
248
|
-
def db_boolean(val)
|
249
|
-
if adapter == :postgres
|
250
|
-
val ? 'TRUE' : 'FALSE'
|
251
|
-
else
|
252
|
-
val ? '1' : '0'
|
253
|
-
end
|
254
|
-
end
|
255
|
-
|
256
139
|
private
|
257
140
|
|
258
141
|
def utf8?
|
@@ -284,6 +167,14 @@ GROUP BY #{ (
|
|
284
167
|
@delta_object = ThinkingSphinx::Deltas.parse self, builder.properties
|
285
168
|
@options = builder.properties
|
286
169
|
|
170
|
+
@model.sphinx_facets ||= []
|
171
|
+
@fields.select { |field| field.faceted }.each { |field|
|
172
|
+
@model.sphinx_facets << field.to_facet
|
173
|
+
}
|
174
|
+
@attributes.select { |attrib| attrib.faceted }.each { |attrib|
|
175
|
+
@model.sphinx_facets << attrib.to_facet
|
176
|
+
}
|
177
|
+
|
287
178
|
# We want to make sure that if the database doesn't exist, then Thinking
|
288
179
|
# Sphinx doesn't mind when running non-TS tasks (like db:create, db:drop
|
289
180
|
# and db:migrate). It's a bit hacky, but I can't think of a better way.
|
@@ -345,12 +236,10 @@ GROUP BY #{ (
|
|
345
236
|
|
346
237
|
def crc_column
|
347
238
|
if @model.column_names.include?(@model.inheritance_column)
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
"CAST(IFNULL(CRC32(#{@model.quoted_table_name}.#{quote_column(@model.inheritance_column)}), #{@model.to_crc32.to_s}) AS UNSIGNED)"
|
353
|
-
end
|
239
|
+
adapter.cast_to_unsigned(adapter.convert_nulls(
|
240
|
+
adapter.crc(adapter.quote_with_table(@model.inheritance_column)),
|
241
|
+
@model.to_crc32
|
242
|
+
))
|
354
243
|
else
|
355
244
|
@model.to_crc32.to_s
|
356
245
|
end
|
@@ -383,24 +272,13 @@ GROUP BY #{ (
|
|
383
272
|
:as => :sphinx_deleted
|
384
273
|
) unless @attributes.detect { |attr| attr.alias == :sphinx_deleted }
|
385
274
|
end
|
386
|
-
|
387
|
-
def riddle_adapter
|
388
|
-
case adapter
|
389
|
-
when :postgres
|
390
|
-
"pgsql"
|
391
|
-
when :mysql
|
392
|
-
"mysql"
|
393
|
-
else
|
394
|
-
raise "Unsupported Database Adapter: Sphinx only supports MySQL and PosgreSQL"
|
395
|
-
end
|
396
|
-
end
|
397
|
-
|
275
|
+
|
398
276
|
def set_source_database_settings(source)
|
399
277
|
config = @model.connection.instance_variable_get(:@config)
|
400
278
|
|
401
|
-
source.sql_host = config[:host]
|
402
|
-
source.sql_user = config[:username]
|
403
|
-
source.sql_pass = (config[:password] || "").gsub('#', '\#')
|
279
|
+
source.sql_host = config[:host] || "localhost"
|
280
|
+
source.sql_user = config[:username] || config[:user]
|
281
|
+
source.sql_pass = (config[:password].to_s || "").gsub('#', '\#')
|
404
282
|
source.sql_db = config[:database]
|
405
283
|
source.sql_port = config[:port]
|
406
284
|
source.sql_sock = config[:socket]
|
@@ -423,9 +301,7 @@ GROUP BY #{ (
|
|
423
301
|
source.sql_query_pre << "SET SESSION group_concat_max_len = #{@options[:group_concat_max_len]}"
|
424
302
|
end
|
425
303
|
|
426
|
-
|
427
|
-
source.sql_query_pre << "SET NAMES utf8"
|
428
|
-
end
|
304
|
+
source.sql_query_pre += [adapter.utf8_query_pre].compact if utf8?
|
429
305
|
end
|
430
306
|
|
431
307
|
def set_source_settings(source)
|
@@ -449,5 +325,89 @@ GROUP BY #{ (
|
|
449
325
|
def sql_query_pre_for_delta
|
450
326
|
[""]
|
451
327
|
end
|
328
|
+
|
329
|
+
# Generates the big SQL statement to get the data back for all the fields
|
330
|
+
# and attributes, using all the relevant association joins. If you want
|
331
|
+
# the version filtered for delta values, send through :delta => true in the
|
332
|
+
# options. Won't do much though if the index isn't set up to support a
|
333
|
+
# delta sibling.
|
334
|
+
#
|
335
|
+
# Examples:
|
336
|
+
#
|
337
|
+
# index.to_sql
|
338
|
+
# index.to_sql(:delta => true)
|
339
|
+
#
|
340
|
+
def to_sql(options={})
|
341
|
+
assocs = all_associations
|
342
|
+
|
343
|
+
where_clause = ""
|
344
|
+
if self.delta? && !@delta_object.clause(@model, options[:delta]).blank?
|
345
|
+
where_clause << " AND #{@delta_object.clause(@model, options[:delta])}"
|
346
|
+
end
|
347
|
+
unless @conditions.empty?
|
348
|
+
where_clause << " AND " << @conditions.join(" AND ")
|
349
|
+
end
|
350
|
+
|
351
|
+
internal_groupings = []
|
352
|
+
if @model.column_names.include?(@model.inheritance_column)
|
353
|
+
internal_groupings << "#{@model.quoted_table_name}.#{quote_column(@model.inheritance_column)}"
|
354
|
+
end
|
355
|
+
|
356
|
+
unique_id_expr = "* #{ThinkingSphinx.indexed_models.size} + #{options[:offset] || 0}"
|
357
|
+
|
358
|
+
sql = <<-SQL
|
359
|
+
SELECT #{ (
|
360
|
+
["#{@model.quoted_table_name}.#{quote_column(@model.primary_key)} #{unique_id_expr} AS #{quote_column(@model.primary_key)} "] +
|
361
|
+
@fields.collect { |field| field.to_select_sql } +
|
362
|
+
@attributes.collect { |attribute| attribute.to_select_sql }
|
363
|
+
).join(", ") }
|
364
|
+
FROM #{ @model.table_name }
|
365
|
+
#{ assocs.collect { |assoc| assoc.to_sql }.join(' ') }
|
366
|
+
WHERE #{@model.quoted_table_name}.#{quote_column(@model.primary_key)} >= $start
|
367
|
+
AND #{@model.quoted_table_name}.#{quote_column(@model.primary_key)} <= $end
|
368
|
+
#{ where_clause }
|
369
|
+
GROUP BY #{ (
|
370
|
+
["#{@model.quoted_table_name}.#{quote_column(@model.primary_key)}"] +
|
371
|
+
@fields.collect { |field| field.to_group_sql }.compact +
|
372
|
+
@attributes.collect { |attribute| attribute.to_group_sql }.compact +
|
373
|
+
@groupings + internal_groupings
|
374
|
+
).join(", ") }
|
375
|
+
SQL
|
376
|
+
|
377
|
+
if @model.connection.class.name == "ActiveRecord::ConnectionAdapters::MysqlAdapter"
|
378
|
+
sql += " ORDER BY NULL"
|
379
|
+
end
|
380
|
+
|
381
|
+
sql
|
382
|
+
end
|
383
|
+
|
384
|
+
# Simple helper method for the query info SQL - which is a statement that
|
385
|
+
# returns the single row for a corresponding id.
|
386
|
+
#
|
387
|
+
def to_sql_query_info(offset)
|
388
|
+
"SELECT * FROM #{@model.quoted_table_name} WHERE " +
|
389
|
+
" #{quote_column(@model.primary_key)} = (($id - #{offset}) / #{ThinkingSphinx.indexed_models.size})"
|
390
|
+
end
|
391
|
+
|
392
|
+
# Simple helper method for the query range SQL - which is a statement that
|
393
|
+
# returns minimum and maximum id values. These can be filtered by delta -
|
394
|
+
# so pass in :delta => true to get the delta version of the SQL.
|
395
|
+
#
|
396
|
+
def to_sql_query_range(options={})
|
397
|
+
min_statement = adapter.convert_nulls(
|
398
|
+
"MIN(#{quote_column(@model.primary_key)})", 1
|
399
|
+
)
|
400
|
+
max_statement = adapter.convert_nulls(
|
401
|
+
"MAX(#{quote_column(@model.primary_key)})", 1
|
402
|
+
)
|
403
|
+
|
404
|
+
sql = "SELECT #{min_statement}, #{max_statement} " +
|
405
|
+
"FROM #{@model.quoted_table_name} "
|
406
|
+
if self.delta? && !@delta_object.clause(@model, options[:delta]).blank?
|
407
|
+
sql << "WHERE #{@delta_object.clause(@model, options[:delta])}"
|
408
|
+
end
|
409
|
+
|
410
|
+
sql
|
411
|
+
end
|
452
412
|
end
|
453
413
|
end
|