pixeltrix-thinking-sphinx 1.1.5 → 1.2.1
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 +147 -0
- data/lib/thinking_sphinx/active_record/attribute_updates.rb +48 -0
- data/lib/thinking_sphinx/active_record/delta.rb +14 -1
- data/lib/thinking_sphinx/active_record/scopes.rb +37 -0
- data/lib/thinking_sphinx/active_record.rb +46 -12
- data/lib/thinking_sphinx/adapters/abstract_adapter.rb +9 -1
- data/lib/thinking_sphinx/adapters/mysql_adapter.rb +3 -2
- data/lib/thinking_sphinx/adapters/postgresql_adapter.rb +12 -5
- data/lib/thinking_sphinx/association.rb +20 -0
- data/lib/thinking_sphinx/attribute.rb +187 -116
- data/lib/thinking_sphinx/class_facet.rb +15 -0
- data/lib/thinking_sphinx/configuration.rb +46 -14
- data/lib/thinking_sphinx/core/string.rb +3 -10
- data/lib/thinking_sphinx/deltas/datetime_delta.rb +3 -3
- data/lib/thinking_sphinx/deltas/default_delta.rb +9 -6
- data/lib/thinking_sphinx/deltas/delayed_delta/delta_job.rb +1 -1
- data/lib/thinking_sphinx/deltas/delayed_delta.rb +4 -2
- data/lib/thinking_sphinx/deltas.rb +14 -6
- data/lib/thinking_sphinx/deploy/capistrano.rb +98 -0
- data/lib/thinking_sphinx/excerpter.rb +22 -0
- data/lib/thinking_sphinx/facet.rb +68 -18
- data/lib/thinking_sphinx/facet_search.rb +134 -0
- data/lib/thinking_sphinx/field.rb +7 -97
- data/lib/thinking_sphinx/index/builder.rb +255 -201
- data/lib/thinking_sphinx/index.rb +28 -343
- data/lib/thinking_sphinx/property.rb +160 -0
- data/lib/thinking_sphinx/rails_additions.rb +7 -4
- data/lib/thinking_sphinx/search.rb +593 -587
- data/lib/thinking_sphinx/search_methods.rb +421 -0
- data/lib/thinking_sphinx/source/internal_properties.rb +46 -0
- data/lib/thinking_sphinx/source/sql.rb +128 -0
- data/lib/thinking_sphinx/source.rb +150 -0
- data/lib/thinking_sphinx/tasks.rb +45 -11
- data/lib/thinking_sphinx.rb +88 -14
- data/rails/init.rb +14 -0
- data/spec/{unit → lib}/thinking_sphinx/active_record/delta_spec.rb +7 -7
- 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 +92 -0
- data/spec/{unit → lib}/thinking_sphinx/active_record_spec.rb +115 -42
- data/spec/{unit → lib}/thinking_sphinx/association_spec.rb +4 -5
- data/spec/lib/thinking_sphinx/attribute_spec.rb +465 -0
- data/spec/{unit → lib}/thinking_sphinx/configuration_spec.rb +118 -7
- 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/lib/thinking_sphinx/facet_spec.rb +302 -0
- data/spec/{unit → lib}/thinking_sphinx/field_spec.rb +26 -17
- data/spec/lib/thinking_sphinx/index/builder_spec.rb +355 -0
- data/spec/{unit → lib}/thinking_sphinx/index/faux_column_spec.rb +0 -0
- data/spec/{unit → lib}/thinking_sphinx/index_spec.rb +3 -12
- data/spec/lib/thinking_sphinx/rails_additions_spec.rb +191 -0
- data/spec/lib/thinking_sphinx/search_methods_spec.rb +152 -0
- data/spec/lib/thinking_sphinx/search_spec.rb +887 -0
- data/spec/lib/thinking_sphinx/source_spec.rb +217 -0
- data/spec/{unit → lib}/thinking_sphinx_spec.rb +30 -8
- data/tasks/distribution.rb +20 -1
- data/tasks/testing.rb +7 -15
- data/vendor/after_commit/init.rb +3 -0
- data/vendor/after_commit/lib/after_commit/active_record.rb +27 -4
- data/vendor/after_commit/lib/after_commit/connection_adapters.rb +1 -1
- data/vendor/after_commit/lib/after_commit.rb +4 -1
- data/vendor/riddle/lib/riddle/client/message.rb +4 -3
- data/vendor/riddle/lib/riddle/client.rb +3 -0
- data/vendor/riddle/lib/riddle/configuration/section.rb +8 -2
- data/vendor/riddle/lib/riddle/controller.rb +1 -1
- data/vendor/riddle/lib/riddle.rb +1 -1
- metadata +75 -39
- data/README +0 -107
- data/lib/thinking_sphinx/active_record/search.rb +0 -57
- data/lib/thinking_sphinx/collection.rb +0 -142
- data/lib/thinking_sphinx/facet_collection.rb +0 -44
- data/spec/unit/thinking_sphinx/active_record/search_spec.rb +0 -107
- data/spec/unit/thinking_sphinx/attribute_spec.rb +0 -212
- data/spec/unit/thinking_sphinx/collection_spec.rb +0 -14
- data/spec/unit/thinking_sphinx/index/builder_spec.rb +0 -5
- data/spec/unit/thinking_sphinx/search_spec.rb +0 -59
@@ -8,15 +8,16 @@ module ThinkingSphinx
|
|
8
8
|
# generate SQL statements, you'll need to set the base model, and all the
|
9
9
|
# associations. Which can get messy. Use Index.link!, it really helps.
|
10
10
|
#
|
11
|
-
class Attribute
|
12
|
-
attr_accessor :
|
11
|
+
class Attribute < ThinkingSphinx::Property
|
12
|
+
attr_accessor :query_source
|
13
13
|
|
14
14
|
# To create a new attribute, you'll need to pass in either a single Column
|
15
15
|
# or an array of them, and some (optional) options.
|
16
16
|
#
|
17
17
|
# Valid options are:
|
18
|
-
# - :as
|
19
|
-
# - :type
|
18
|
+
# - :as => :alias_name
|
19
|
+
# - :type => :attribute_type
|
20
|
+
# - :source => :field, :query, :ranged_query
|
20
21
|
#
|
21
22
|
# Alias is only required in three circumstances: when there's
|
22
23
|
# another attribute or field with the same name, when the column name is
|
@@ -28,6 +29,13 @@ module ThinkingSphinx
|
|
28
29
|
# can't be figured out by the column - ie: when not actually using a
|
29
30
|
# database column as your source.
|
30
31
|
#
|
32
|
+
# Source is only used for multi-value attributes (MVA). By default this will
|
33
|
+
# use a left-join and a group_concat to obtain the values. For better performance
|
34
|
+
# during indexing it can be beneficial to let Sphinx use a separate query to retrieve
|
35
|
+
# all document,value-pairs.
|
36
|
+
# Either :query or :ranged_query will enable this feature, where :ranged_query will cause
|
37
|
+
# the query to be executed incremental.
|
38
|
+
#
|
31
39
|
# Example usage:
|
32
40
|
#
|
33
41
|
# Attribute.new(
|
@@ -40,6 +48,12 @@ module ThinkingSphinx
|
|
40
48
|
# )
|
41
49
|
#
|
42
50
|
# Attribute.new(
|
51
|
+
# Column.new(:posts, :id),
|
52
|
+
# :as => :post_ids,
|
53
|
+
# :source => :ranged_query
|
54
|
+
# )
|
55
|
+
#
|
56
|
+
# Attribute.new(
|
43
57
|
# [Column.new(:pages, :id), Column.new(:articles, :id)],
|
44
58
|
# :as => :content_ids
|
45
59
|
# )
|
@@ -53,15 +67,17 @@ module ThinkingSphinx
|
|
53
67
|
# If you're creating attributes for latitude and longitude, don't forget
|
54
68
|
# that Sphinx expects these values to be in radians.
|
55
69
|
#
|
56
|
-
def initialize(columns, options = {})
|
57
|
-
|
58
|
-
@associations = {}
|
70
|
+
def initialize(source, columns, options = {})
|
71
|
+
super
|
59
72
|
|
60
|
-
|
73
|
+
@type = options[:type]
|
74
|
+
@query_source = options[:source]
|
75
|
+
@crc = options[:crc]
|
61
76
|
|
62
|
-
@
|
63
|
-
@type
|
64
|
-
|
77
|
+
@type ||= :multi unless @query_source.nil?
|
78
|
+
@type = :integer if @type == :string && @crc
|
79
|
+
|
80
|
+
source.attributes << self
|
65
81
|
end
|
66
82
|
|
67
83
|
# Get the part of the SELECT clause related to this attribute. Don't forget
|
@@ -71,37 +87,30 @@ module ThinkingSphinx
|
|
71
87
|
# datetimes to timestamps, as needed.
|
72
88
|
#
|
73
89
|
def to_select_sql
|
90
|
+
return nil unless include_as_association?
|
91
|
+
|
92
|
+
separator = all_ints? || all_datetimes? || @crc ? ',' : ' '
|
93
|
+
|
74
94
|
clause = @columns.collect { |column|
|
75
|
-
column_with_prefix(column)
|
95
|
+
part = column_with_prefix(column)
|
96
|
+
case type
|
97
|
+
when :string
|
98
|
+
adapter.convert_nulls(part)
|
99
|
+
when :datetime
|
100
|
+
adapter.cast_to_datetime(part)
|
101
|
+
else
|
102
|
+
part
|
103
|
+
end
|
76
104
|
}.join(', ')
|
77
105
|
|
78
|
-
|
79
|
-
|
106
|
+
# clause = adapter.cast_to_datetime(clause) if type == :datetime
|
107
|
+
clause = adapter.crc(clause) if @crc
|
80
108
|
clause = adapter.concatenate(clause, separator) if concat_ws?
|
81
109
|
clause = adapter.group_concatenate(clause, separator) if is_many?
|
82
|
-
clause = adapter.cast_to_datetime(clause) if type == :datetime
|
83
|
-
clause = adapter.convert_nulls(clause) if type == :string
|
84
110
|
|
85
111
|
"#{clause} AS #{quote_column(unique_name)}"
|
86
112
|
end
|
87
113
|
|
88
|
-
# Get the part of the GROUP BY clause related to this attribute - if one is
|
89
|
-
# needed. If not, all you'll get back is nil. The latter will happen if
|
90
|
-
# there isn't actually a real column to get data from, or if there's
|
91
|
-
# multiple data values (read: a has_many or has_and_belongs_to_many
|
92
|
-
# association).
|
93
|
-
#
|
94
|
-
def to_group_sql
|
95
|
-
case
|
96
|
-
when is_many?, is_string?, ThinkingSphinx.use_group_by_shortcut?
|
97
|
-
nil
|
98
|
-
else
|
99
|
-
@columns.collect { |column|
|
100
|
-
column_with_prefix(column)
|
101
|
-
}
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
114
|
def type_to_config
|
106
115
|
{
|
107
116
|
:multi => :sql_attr_multi,
|
@@ -113,127 +122,177 @@ module ThinkingSphinx
|
|
113
122
|
}[type]
|
114
123
|
end
|
115
124
|
|
116
|
-
def
|
117
|
-
|
118
|
-
"uint #{unique_name} from field"
|
119
|
-
else
|
120
|
-
unique_name
|
121
|
-
end
|
125
|
+
def include_as_association?
|
126
|
+
! (type == :multi && (query_source == :query || query_source == :ranged_query))
|
122
127
|
end
|
123
128
|
|
124
|
-
# Returns the
|
125
|
-
# the attribute
|
126
|
-
#
|
127
|
-
#
|
129
|
+
# Returns the configuration value that should be used for
|
130
|
+
# the attribute.
|
131
|
+
# Special case is the multi-valued attribute that needs some
|
132
|
+
# extra configuration.
|
128
133
|
#
|
129
|
-
def
|
130
|
-
if
|
131
|
-
|
134
|
+
def config_value(offset = nil, delta = false)
|
135
|
+
if type == :multi
|
136
|
+
multi_config = include_as_association? ? "field" :
|
137
|
+
source_value(offset, delta).gsub(/\s+/m, " ").strip
|
138
|
+
"uint #{unique_name} from #{multi_config}"
|
132
139
|
else
|
133
|
-
|
140
|
+
unique_name
|
134
141
|
end
|
135
142
|
end
|
136
|
-
|
143
|
+
|
137
144
|
# Returns the type of the column. If that's not already set, it returns
|
138
145
|
# :multi if there's the possibility of more than one value, :string if
|
139
146
|
# there's more than one association, otherwise it figures out what the
|
140
147
|
# actual column's datatype is and returns that.
|
148
|
+
#
|
141
149
|
def type
|
142
|
-
@type ||=
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
150
|
+
@type ||= begin
|
151
|
+
base_type = case
|
152
|
+
when is_many_datetimes?
|
153
|
+
:datetime
|
154
|
+
when is_many?, is_many_ints?
|
155
|
+
:multi
|
156
|
+
when @associations.values.flatten.length > 1
|
157
|
+
:string
|
158
|
+
else
|
159
|
+
translated_type_from_database
|
160
|
+
end
|
161
|
+
|
162
|
+
if base_type == :string && @crc
|
163
|
+
:integer
|
164
|
+
else
|
165
|
+
@crc = false
|
166
|
+
base_type
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def updatable?
|
172
|
+
[:integer, :datetime, :boolean].include?(type) && !is_string?
|
173
|
+
end
|
174
|
+
|
175
|
+
def live_value(instance)
|
176
|
+
object = instance
|
177
|
+
column = @columns.first
|
178
|
+
column.__stack.each { |method| object = object.send(method) }
|
179
|
+
object.send(column.__name)
|
180
|
+
end
|
181
|
+
|
182
|
+
def all_ints?
|
183
|
+
all_of_type?(:integer)
|
184
|
+
end
|
185
|
+
|
186
|
+
def all_datetimes?
|
187
|
+
all_of_type?(:datetime, :date, :timestamp)
|
188
|
+
end
|
189
|
+
|
190
|
+
private
|
191
|
+
|
192
|
+
def source_value(offset, delta)
|
193
|
+
if is_string?
|
194
|
+
return "#{query_source.to_s.dasherize}; #{columns.first.__name}"
|
195
|
+
end
|
196
|
+
|
197
|
+
query = query(offset)
|
198
|
+
|
199
|
+
if query_source == :ranged_query
|
200
|
+
query += query_clause
|
201
|
+
query += " AND #{query_delta.strip}" if delta
|
202
|
+
"ranged-query; #{query}; #{range_query}"
|
147
203
|
else
|
148
|
-
|
204
|
+
query += "WHERE #{query_delta.strip}" if delta
|
205
|
+
"query; #{query}"
|
149
206
|
end
|
150
207
|
end
|
151
208
|
|
152
|
-
def
|
153
|
-
|
209
|
+
def query(offset)
|
210
|
+
base_assoc = base_association_for_mva
|
211
|
+
end_assoc = end_association_for_mva
|
212
|
+
raise "Could not determine SQL for MVA" if base_assoc.nil?
|
154
213
|
|
155
|
-
|
214
|
+
<<-SQL
|
215
|
+
SELECT #{foreign_key_for_mva base_assoc}
|
216
|
+
#{ThinkingSphinx.unique_id_expression(offset)} AS #{quote_column('id')},
|
217
|
+
#{primary_key_for_mva(end_assoc)} AS #{quote_column(unique_name)}
|
218
|
+
FROM #{quote_table_name base_assoc.table} #{association_joins}
|
219
|
+
SQL
|
156
220
|
end
|
157
221
|
|
158
|
-
|
222
|
+
def query_clause
|
223
|
+
foreign_key = foreign_key_for_mva base_association_for_mva
|
224
|
+
"WHERE #{foreign_key} >= $start AND #{foreign_key} <= $end"
|
225
|
+
end
|
159
226
|
|
160
|
-
def
|
161
|
-
|
227
|
+
def query_delta
|
228
|
+
foreign_key = foreign_key_for_mva base_association_for_mva
|
229
|
+
<<-SQL
|
230
|
+
#{foreign_key} IN (SELECT #{quote_column model.primary_key}
|
231
|
+
FROM #{model.quoted_table_name}
|
232
|
+
WHERE #{@source.index.delta_object.clause(model, true)})
|
233
|
+
SQL
|
162
234
|
end
|
163
235
|
|
164
|
-
def
|
165
|
-
|
236
|
+
def range_query
|
237
|
+
assoc = base_association_for_mva
|
238
|
+
foreign_key = foreign_key_for_mva assoc
|
239
|
+
"SELECT MIN(#{foreign_key}), MAX(#{foreign_key}) FROM #{quote_table_name assoc.table}"
|
166
240
|
end
|
167
241
|
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
def concat_ws?
|
173
|
-
multiple_associations? || @columns.length > 1
|
242
|
+
def primary_key_for_mva(assoc)
|
243
|
+
quote_with_table(
|
244
|
+
assoc.table, assoc.primary_key_from_reflection || columns.first.__name
|
245
|
+
)
|
174
246
|
end
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
#
|
179
|
-
def multiple_associations?
|
180
|
-
associations.any? { |col,assocs| assocs.length > 1 }
|
247
|
+
|
248
|
+
def foreign_key_for_mva(assoc)
|
249
|
+
quote_with_table assoc.table, assoc.reflection.primary_key_name
|
181
250
|
end
|
182
251
|
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
nil
|
198
|
-
}.compact.join(', ')
|
252
|
+
def end_association_for_mva
|
253
|
+
@association_for_mva ||= associations[columns.first].detect { |assoc|
|
254
|
+
assoc.has_column?(columns.first.__name)
|
255
|
+
}
|
256
|
+
end
|
257
|
+
|
258
|
+
def base_association_for_mva
|
259
|
+
@first_association_for_mva ||= begin
|
260
|
+
assoc = end_association_for_mva
|
261
|
+
while !assoc.parent.nil?
|
262
|
+
assoc = assoc.parent
|
263
|
+
end
|
264
|
+
|
265
|
+
assoc
|
199
266
|
end
|
200
267
|
end
|
201
268
|
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
269
|
+
def association_joins
|
270
|
+
joins = []
|
271
|
+
assoc = end_association_for_mva
|
272
|
+
while assoc != base_association_for_mva
|
273
|
+
joins << assoc.to_sql
|
274
|
+
assoc = assoc.parent
|
275
|
+
end
|
276
|
+
|
277
|
+
joins.join(' ')
|
207
278
|
end
|
208
279
|
|
209
280
|
def is_many_ints?
|
210
281
|
concat_ws? && all_ints?
|
211
282
|
end
|
212
283
|
|
213
|
-
|
214
|
-
|
215
|
-
def is_string?
|
216
|
-
columns.all? { |col| col.is_string? }
|
284
|
+
def is_many_datetimes?
|
285
|
+
is_many? && all_datetimes?
|
217
286
|
end
|
218
|
-
|
219
|
-
def all_ints?
|
220
|
-
@columns.all? { |col|
|
221
|
-
klasses = @associations[col].empty? ? [@model] :
|
222
|
-
@associations[col].collect { |assoc| assoc.reflection.klass }
|
223
|
-
klasses.all? { |klass|
|
224
|
-
column = klass.columns.detect { |column| column.name == col.__name.to_s }
|
225
|
-
!column.nil? && column.type == :integer
|
226
|
-
}
|
227
|
-
}
|
228
|
-
end
|
229
|
-
|
287
|
+
|
230
288
|
def type_from_database
|
231
289
|
klass = @associations.values.flatten.first ?
|
232
290
|
@associations.values.flatten.first.reflection.klass : @model
|
233
291
|
|
234
|
-
klass.columns.detect { |col|
|
292
|
+
column = klass.columns.detect { |col|
|
235
293
|
@columns.collect { |c| c.__name.to_s }.include? col.name
|
236
|
-
}
|
294
|
+
}
|
295
|
+
column.nil? ? nil : column.type
|
237
296
|
end
|
238
297
|
|
239
298
|
def translated_type_from_database
|
@@ -247,12 +306,24 @@ module ThinkingSphinx
|
|
247
306
|
else
|
248
307
|
raise <<-MESSAGE
|
249
308
|
|
250
|
-
Cannot automatically map
|
251
|
-
type (integer, float, boolean, datetime, string as ordinal).
|
252
|
-
explicitly convert the column's value in your define_index
|
309
|
+
Cannot automatically map attribute #{unique_name} in #{@model.name} to an
|
310
|
+
equivalent Sphinx type (integer, float, boolean, datetime, string as ordinal).
|
311
|
+
You could try to explicitly convert the column's value in your define_index
|
312
|
+
block:
|
253
313
|
has "CAST(column AS INT)", :type => :integer, :as => :column
|
254
314
|
MESSAGE
|
255
315
|
end
|
256
316
|
end
|
317
|
+
|
318
|
+
def all_of_type?(*column_types)
|
319
|
+
@columns.all? { |col|
|
320
|
+
klasses = @associations[col].empty? ? [@model] :
|
321
|
+
@associations[col].collect { |assoc| assoc.reflection.klass }
|
322
|
+
klasses.all? { |klass|
|
323
|
+
column = klass.columns.detect { |column| column.name == col.__name.to_s }
|
324
|
+
!column.nil? && column_types.include?(column.type)
|
325
|
+
}
|
326
|
+
}
|
327
|
+
end
|
257
328
|
end
|
258
329
|
end
|
@@ -19,20 +19,25 @@ module ThinkingSphinx
|
|
19
19
|
# min infix length:: 1
|
20
20
|
# mem limit:: 64M
|
21
21
|
# max matches:: 1000
|
22
|
-
# morphology::
|
22
|
+
# morphology:: nil
|
23
23
|
# charset type:: utf-8
|
24
24
|
# charset table:: nil
|
25
25
|
# ignore chars:: nil
|
26
26
|
# html strip:: false
|
27
27
|
# html remove elements:: ''
|
28
|
+
# searchd_binary_name:: searchd
|
29
|
+
# indexer_binary_name:: indexer
|
28
30
|
#
|
29
31
|
# If you want to change these settings, create a YAML file at
|
30
32
|
# config/sphinx.yml with settings for each environment, in a similar
|
31
33
|
# fashion to database.yml - using the following keys: config_file,
|
32
34
|
# searchd_log_file, query_log_file, pid_file, searchd_file_path, port,
|
33
35
|
# allow_star, enable_star, min_prefix_len, min_infix_len, mem_limit,
|
34
|
-
# max_matches,
|
35
|
-
# html_strip,
|
36
|
+
# max_matches, morphology, charset_type, charset_table, ignore_chars,
|
37
|
+
# html_strip, html_remove_elements, delayed_job_priority,
|
38
|
+
# searchd_binary_name, indexer_binary_name.
|
39
|
+
#
|
40
|
+
# I think you've got the idea.
|
36
41
|
#
|
37
42
|
# Each setting in the YAML file is optional - so only put in the ones you
|
38
43
|
# want to change.
|
@@ -52,10 +57,13 @@ module ThinkingSphinx
|
|
52
57
|
min_infix_len min_prefix_len min_word_len mlock morphology ngram_chars
|
53
58
|
ngram_len phrase_boundary phrase_boundary_step preopen stopwords
|
54
59
|
wordforms )
|
60
|
+
|
61
|
+
CustomOptions = %w( disable_range )
|
55
62
|
|
56
63
|
attr_accessor :config_file, :searchd_log_file, :query_log_file,
|
57
64
|
:pid_file, :searchd_file_path, :address, :port, :allow_star,
|
58
|
-
:database_yml_file, :app_root, :bin_path, :model_directories
|
65
|
+
:database_yml_file, :app_root, :bin_path, :model_directories,
|
66
|
+
:delayed_job_priority, :searchd_binary_name, :indexer_binary_name
|
59
67
|
|
60
68
|
attr_accessor :source_options, :index_options
|
61
69
|
|
@@ -68,10 +76,19 @@ module ThinkingSphinx
|
|
68
76
|
self.reset
|
69
77
|
end
|
70
78
|
|
71
|
-
def
|
72
|
-
|
73
|
-
|
74
|
-
|
79
|
+
def self.configure(&block)
|
80
|
+
yield instance
|
81
|
+
instance.reset(instance.app_root)
|
82
|
+
end
|
83
|
+
|
84
|
+
def reset(custom_app_root=nil)
|
85
|
+
if custom_app_root
|
86
|
+
self.app_root = custom_app_root
|
87
|
+
else
|
88
|
+
self.app_root = RAILS_ROOT if defined?(RAILS_ROOT)
|
89
|
+
self.app_root = Merb.root if defined?(Merb)
|
90
|
+
self.app_root ||= app_root
|
91
|
+
end
|
75
92
|
|
76
93
|
@configuration = Riddle::Configuration.new
|
77
94
|
@configuration.searchd.address = "127.0.0.1"
|
@@ -85,13 +102,17 @@ module ThinkingSphinx
|
|
85
102
|
self.searchd_file_path = "#{self.app_root}/db/sphinx/#{environment}"
|
86
103
|
self.allow_star = false
|
87
104
|
self.bin_path = ""
|
88
|
-
self.model_directories = ["#{app_root}/app/models/"]
|
105
|
+
self.model_directories = ["#{app_root}/app/models/"] +
|
106
|
+
Dir.glob("#{app_root}/vendor/plugins/*/app/models/")
|
107
|
+
self.delayed_job_priority = 0
|
89
108
|
|
90
109
|
self.source_options = {}
|
91
110
|
self.index_options = {
|
92
|
-
:charset_type => "utf-8"
|
93
|
-
:morphology => "stem_en"
|
111
|
+
:charset_type => "utf-8"
|
94
112
|
}
|
113
|
+
|
114
|
+
self.searchd_binary_name = "searchd"
|
115
|
+
self.indexer_binary_name = "indexer"
|
95
116
|
|
96
117
|
parse_config
|
97
118
|
|
@@ -136,6 +157,8 @@ module ThinkingSphinx
|
|
136
157
|
# messy dependencies issues).
|
137
158
|
#
|
138
159
|
def load_models
|
160
|
+
return if defined?(Rails) && Rails.configuration.cache_classes
|
161
|
+
|
139
162
|
self.model_directories.each do |base|
|
140
163
|
Dir["#{base}**/*.rb"].each do |file|
|
141
164
|
model_name = file.gsub(/^#{base}([\w_\/\\]+)\.rb/, '\1')
|
@@ -151,6 +174,8 @@ module ThinkingSphinx
|
|
151
174
|
model_name.gsub!(/.*[\/\\]/, '').nil? ? next : retry
|
152
175
|
rescue NameError
|
153
176
|
next
|
177
|
+
rescue StandardError
|
178
|
+
puts "Warning: Error loading #{file}"
|
154
179
|
end
|
155
180
|
end
|
156
181
|
end
|
@@ -196,6 +221,12 @@ module ThinkingSphinx
|
|
196
221
|
@configuration.searchd.query_log = file
|
197
222
|
end
|
198
223
|
|
224
|
+
def client
|
225
|
+
client = Riddle::Client.new address, port
|
226
|
+
client.max_matches = configuration.searchd.max_matches || 1000
|
227
|
+
client
|
228
|
+
end
|
229
|
+
|
199
230
|
private
|
200
231
|
|
201
232
|
# Parse the config/sphinx.yml file - if it exists - then use the attribute
|
@@ -208,10 +239,11 @@ module ThinkingSphinx
|
|
208
239
|
conf = YAML::load(ERB.new(IO.read(path)).result)[environment]
|
209
240
|
|
210
241
|
conf.each do |key,value|
|
211
|
-
self.send("#{key}=", value) if self.
|
242
|
+
self.send("#{key}=", value) if self.respond_to?("#{key}=")
|
212
243
|
|
213
244
|
set_sphinx_setting self.source_options, key, value, SourceOptions
|
214
245
|
set_sphinx_setting self.index_options, key, value, IndexOptions
|
246
|
+
set_sphinx_setting self.index_options, key, value, CustomOptions
|
215
247
|
set_sphinx_setting @configuration.searchd, key, value
|
216
248
|
set_sphinx_setting @configuration.indexer, key, value
|
217
249
|
end unless conf.nil?
|
@@ -228,8 +260,8 @@ module ThinkingSphinx
|
|
228
260
|
if object.is_a?(Hash)
|
229
261
|
object[key.to_sym] = value if allowed.include?(key.to_s)
|
230
262
|
else
|
231
|
-
object.send("#{key}=", value) if object.
|
232
|
-
send("#{key}=", value) if self.
|
263
|
+
object.send("#{key}=", value) if object.respond_to?("#{key}")
|
264
|
+
send("#{key}=", value) if self.respond_to?("#{key}")
|
233
265
|
end
|
234
266
|
end
|
235
267
|
end
|
@@ -1,18 +1,11 @@
|
|
1
|
+
require 'zlib'
|
2
|
+
|
1
3
|
module ThinkingSphinx
|
2
4
|
module Core
|
3
5
|
module String
|
4
|
-
|
5
6
|
def to_crc32
|
6
|
-
|
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
|
7
|
+
Zlib.crc32 self
|
14
8
|
end
|
15
|
-
|
16
9
|
end
|
17
10
|
end
|
18
11
|
end
|
@@ -18,8 +18,8 @@ module ThinkingSphinx
|
|
18
18
|
config = ThinkingSphinx::Configuration.instance
|
19
19
|
rotate = ThinkingSphinx.sphinx_running? ? "--rotate" : ""
|
20
20
|
|
21
|
-
output = `#{config.bin_path}
|
22
|
-
output += `#{config.bin_path}
|
21
|
+
output = `#{config.bin_path}#{config.indexer_binary_name} --config #{config.config_file} #{rotate} #{delta_index_name model}`
|
22
|
+
output += `#{config.bin_path}#{config.indexer_binary_name} --config #{config.config_file} #{rotate} --merge #{core_index_name model} #{delta_index_name model} --merge-dst-range sphinx_deleted 0 0`
|
23
23
|
puts output unless ThinkingSphinx.suppress_delta_output?
|
24
24
|
|
25
25
|
true
|
@@ -39,7 +39,7 @@ module ThinkingSphinx
|
|
39
39
|
|
40
40
|
def clause(model, toggled)
|
41
41
|
if toggled
|
42
|
-
"#{model.quoted_table_name}.#{
|
42
|
+
"#{model.quoted_table_name}.#{model.connection.quote_column_name(@column.to_s)}" +
|
43
43
|
" > #{adapter.time_difference(@threshold)}"
|
44
44
|
else
|
45
45
|
nil
|
@@ -11,18 +11,20 @@ module ThinkingSphinx
|
|
11
11
|
def index(model, instance = nil)
|
12
12
|
return true unless ThinkingSphinx.updates_enabled? &&
|
13
13
|
ThinkingSphinx.deltas_enabled?
|
14
|
+
return true if instance && !toggled(instance)
|
14
15
|
|
15
16
|
config = ThinkingSphinx::Configuration.instance
|
16
17
|
client = Riddle::Client.new config.address, config.port
|
18
|
+
rotate = ThinkingSphinx.sphinx_running? ? "--rotate" : ""
|
19
|
+
|
20
|
+
output = `#{config.bin_path}#{config.indexer_binary_name} --config #{config.config_file} #{rotate} #{delta_index_name model}`
|
21
|
+
puts(output) unless ThinkingSphinx.suppress_delta_output?
|
17
22
|
|
18
23
|
client.update(
|
19
24
|
core_index_name(model),
|
20
25
|
['sphinx_deleted'],
|
21
26
|
{instance.sphinx_document_id => [1]}
|
22
|
-
) if instance && ThinkingSphinx.sphinx_running? && instance.
|
23
|
-
|
24
|
-
output = `#{config.bin_path}indexer --config #{config.config_file} --rotate #{delta_index_name model}`
|
25
|
-
puts output unless ThinkingSphinx.suppress_delta_output?
|
27
|
+
) if instance && ThinkingSphinx.sphinx_running? && instance.in_both_indexes?
|
26
28
|
|
27
29
|
true
|
28
30
|
end
|
@@ -37,11 +39,12 @@ module ThinkingSphinx
|
|
37
39
|
|
38
40
|
def reset_query(model)
|
39
41
|
"UPDATE #{model.quoted_table_name} SET " +
|
40
|
-
"#{
|
42
|
+
"#{model.connection.quote_column_name(@column.to_s)} = #{adapter.boolean(false)} " +
|
43
|
+
"WHERE #{model.connection.quote_column_name(@column.to_s)} = #{adapter.boolean(true)}"
|
41
44
|
end
|
42
45
|
|
43
46
|
def clause(model, toggled)
|
44
|
-
"#{model.quoted_table_name}.#{
|
47
|
+
"#{model.quoted_table_name}.#{model.connection.quote_column_name(@column.to_s)}" +
|
45
48
|
" = #{adapter.boolean(toggled)}"
|
46
49
|
end
|
47
50
|
|
@@ -14,7 +14,7 @@ module ThinkingSphinx
|
|
14
14
|
config = ThinkingSphinx::Configuration.instance
|
15
15
|
client = Riddle::Client.new config.address, config.port
|
16
16
|
|
17
|
-
output = `#{config.bin_path}
|
17
|
+
output = `#{config.bin_path}#{config.indexer_binary_name} --config #{config.config_file} --rotate #{index}`
|
18
18
|
puts output unless ThinkingSphinx.suppress_delta_output?
|
19
19
|
|
20
20
|
true
|
@@ -9,13 +9,15 @@ module ThinkingSphinx
|
|
9
9
|
class DelayedDelta < ThinkingSphinx::Deltas::DefaultDelta
|
10
10
|
def index(model, instance = nil)
|
11
11
|
ThinkingSphinx::Deltas::Job.enqueue(
|
12
|
-
ThinkingSphinx::Deltas::DeltaJob.new(delta_index_name(model))
|
12
|
+
ThinkingSphinx::Deltas::DeltaJob.new(delta_index_name(model)),
|
13
|
+
ThinkingSphinx::Configuration.instance.delayed_job_priority
|
13
14
|
)
|
14
15
|
|
15
16
|
Delayed::Job.enqueue(
|
16
17
|
ThinkingSphinx::Deltas::FlagAsDeletedJob.new(
|
17
18
|
core_index_name(model), instance.sphinx_document_id
|
18
|
-
)
|
19
|
+
),
|
20
|
+
ThinkingSphinx::Configuration.instance.delayed_job_priority
|
19
21
|
) if instance
|
20
22
|
|
21
23
|
true
|