DrMark-thinking-sphinx 1.1.6 → 1.1.14
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 → README.textile} +84 -84
- data/lib/thinking_sphinx/active_record/attribute_updates.rb +48 -0
- data/lib/thinking_sphinx/active_record/delta.rb +10 -1
- data/lib/thinking_sphinx/active_record.rb +10 -3
- data/lib/thinking_sphinx/adapters/postgresql_adapter.rb +1 -1
- data/lib/thinking_sphinx/attribute.rb +44 -134
- data/lib/thinking_sphinx/class_facet.rb +15 -0
- data/lib/thinking_sphinx/collection.rb +1 -0
- data/lib/thinking_sphinx/configuration.rb +7 -3
- data/lib/thinking_sphinx/deltas/datetime_delta.rb +1 -1
- data/lib/thinking_sphinx/deltas/default_delta.rb +3 -2
- data/lib/thinking_sphinx/deltas/delayed_delta.rb +4 -2
- data/lib/thinking_sphinx/deltas.rb +9 -6
- data/lib/thinking_sphinx/deploy/capistrano.rb +82 -0
- data/lib/thinking_sphinx/facet.rb +68 -18
- data/lib/thinking_sphinx/facet_collection.rb +16 -17
- data/lib/thinking_sphinx/field.rb +7 -97
- data/lib/thinking_sphinx/index/builder.rb +255 -232
- data/lib/thinking_sphinx/index.rb +37 -349
- data/lib/thinking_sphinx/property.rb +160 -0
- data/lib/thinking_sphinx/search/facets.rb +98 -0
- data/lib/thinking_sphinx/search.rb +4 -73
- data/lib/thinking_sphinx/source/internal_properties.rb +46 -0
- data/lib/thinking_sphinx/source/sql.rb +124 -0
- data/lib/thinking_sphinx/source.rb +150 -0
- data/lib/thinking_sphinx/tasks.rb +1 -1
- data/lib/thinking_sphinx.rb +3 -1
- data/spec/unit/thinking_sphinx/active_record_spec.rb +14 -12
- data/spec/unit/thinking_sphinx/attribute_spec.rb +16 -11
- data/spec/unit/thinking_sphinx/facet_collection_spec.rb +64 -0
- data/spec/unit/thinking_sphinx/facet_spec.rb +278 -0
- data/spec/unit/thinking_sphinx/field_spec.rb +18 -9
- data/spec/unit/thinking_sphinx/index/builder_spec.rb +347 -1
- data/spec/unit/thinking_sphinx/index_spec.rb +22 -27
- data/spec/unit/thinking_sphinx/rails_additions_spec.rb +183 -0
- data/spec/unit/thinking_sphinx/search_spec.rb +71 -0
- data/spec/unit/thinking_sphinx/source_spec.rb +156 -0
- data/tasks/distribution.rb +1 -1
- data/tasks/testing.rb +7 -15
- metadata +19 -3
data/{README → README.textile}
RENAMED
@@ -1,12 +1,12 @@
|
|
1
|
-
|
1
|
+
h1. Thinking Sphinx
|
2
2
|
|
3
|
-
|
3
|
+
h2. Usage
|
4
4
|
|
5
|
-
First, if you haven't done so already, check out the main usage
|
5
|
+
First, if you haven't done so already, check out the main "usage":http://ts.freelancing-gods.com/usage.html page. Once you've done that, the next place to look for information is the specific method docs - ThinkingSphinx::Search and ThinkingSphinx::Index::Builder in particular.
|
6
6
|
|
7
7
|
Keep in mind that while Thinking Sphinx works for ActiveRecord with Merb, it doesn't yet support DataMapper (although that is planned).
|
8
8
|
|
9
|
-
|
9
|
+
h2. Contributing
|
10
10
|
|
11
11
|
Fork on GitHub and after you've committed tested patches, send a pull request.
|
12
12
|
|
@@ -26,102 +26,102 @@ Then install the ginger gem. The steps are the same, except that you might need
|
|
26
26
|
rake gem
|
27
27
|
sudo gem install pkg/ginger-1.1.0.gem
|
28
28
|
|
29
|
-
Then install the faker gem:
|
30
|
-
|
31
|
-
sudo gem install faker
|
32
|
-
|
33
29
|
Then set up your database:
|
34
30
|
|
35
31
|
cp spec/fixtures/database.yml.default spec/fixtures/database.yml
|
36
|
-
cp spec/fixtures/database.yml.default features/support/db/database.yml
|
37
32
|
mysqladmin -u root create thinking_sphinx
|
38
|
-
|
33
|
+
|
39
34
|
Make sure you don't have another Sphinx daemon (searchd) running. If you do, quit it with "rake ts:stop"
|
40
35
|
in the app root.
|
41
36
|
|
42
37
|
You should now have a passing test suite from which to build your patch on.
|
43
38
|
|
44
39
|
rake spec
|
45
|
-
|
46
|
-
|
40
|
+
|
47
41
|
If you get the message "Failed to start searchd daemon", run the spec with sudo:
|
48
42
|
|
49
43
|
sudo rake spec
|
50
|
-
|
51
|
-
|
44
|
+
|
52
45
|
If you quit the spec suite before it's completed, you may be left with data in the test
|
53
46
|
database, causing the next run to have failures. Let that run complete and then try again.
|
54
47
|
|
55
|
-
|
48
|
+
h2. Contributors
|
56
49
|
|
57
50
|
Since I first released this library, there's been quite a few people who have submitted patches, to my immense gratitude. Others have suggested syntax changes and general improvements. So my thanks to the following people:
|
58
51
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
52
|
+
* Joost Hietbrink
|
53
|
+
* Jonathan Conway
|
54
|
+
* Gregory Mirzayantz
|
55
|
+
* Tung Nguyen
|
56
|
+
* Sean Cribbs
|
57
|
+
* Benoit Caccinolo
|
58
|
+
* John Barton
|
59
|
+
* Oliver Beddows
|
60
|
+
* Arthur Zapparoli
|
61
|
+
* Dusty Doris
|
62
|
+
* Marcus Crafter
|
63
|
+
* Patrick Lenz
|
64
|
+
* Björn Andreasson
|
65
|
+
* James Healy
|
66
|
+
* Jae-Jun Hwang
|
67
|
+
* Xavier Shay
|
68
|
+
* Jason Rust
|
69
|
+
* Gopal Patel
|
70
|
+
* Chris Heald
|
71
|
+
* Peter Vandenberk
|
72
|
+
* Josh French
|
73
|
+
* Andrew Bennett
|
74
|
+
* Jordan Fowler
|
75
|
+
* Seth Walker
|
76
|
+
* Joe Noon
|
77
|
+
* Wolfgang Postler
|
78
|
+
* Rick Olson
|
79
|
+
* Killian Murphy
|
80
|
+
* Morten Primdahl
|
81
|
+
* Ryan Bates
|
82
|
+
* David Eisinger
|
83
|
+
* Shay Arnett
|
84
|
+
* Minh Tran
|
85
|
+
* Jeremy Durham
|
86
|
+
* Piotr Sarnacki
|
87
|
+
* Matt Johnson
|
88
|
+
* Nicolas Blanco
|
89
|
+
* Max Lapshin
|
90
|
+
* Josh Natanson
|
91
|
+
* Philip Hallstrom
|
92
|
+
* Christian Rishøj
|
93
|
+
* Mike Flester
|
94
|
+
* Jim Remsik
|
95
|
+
* Kennon Ballou
|
96
|
+
* Henrik Nyh
|
97
|
+
* Emil Tin
|
98
|
+
* Doug Cole
|
99
|
+
* Ed Hickey
|
100
|
+
* Evan Weaver
|
101
|
+
* Thibaut Barrere
|
102
|
+
* Kristopher Chambers
|
103
|
+
* Dmitrij Smalko
|
104
|
+
* Aleksey Yeschenko
|
105
|
+
* Lachie Cox
|
106
|
+
* Lourens Naude
|
107
|
+
* Tom Davies
|
108
|
+
* Dan Pickett
|
109
|
+
* Alex Caudill
|
110
|
+
* Jim Benton
|
111
|
+
* John Aughey
|
112
|
+
* Keith Pitty
|
113
|
+
* Jeff Talbot
|
114
|
+
* Dana Contreras
|
115
|
+
* Menno van der Sman
|
116
|
+
* Bill Harding
|
117
|
+
* Isaac Feliu
|
118
|
+
* Andrei Bocan
|
119
|
+
* László Bácsi
|
120
|
+
* Peter Wagenet
|
121
|
+
* Max Lapshin
|
122
|
+
* Martin Emde
|
123
|
+
* David Wennergren
|
124
|
+
* Mark Lane
|
125
|
+
* Eric Lindvall
|
126
|
+
* Lawrence Pit
|
127
|
+
* Mike Bailey
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module ThinkingSphinx
|
2
|
+
module ActiveRecord
|
3
|
+
module AttributeUpdates
|
4
|
+
def self.included(base)
|
5
|
+
base.class_eval do
|
6
|
+
after_commit :update_attribute_values
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def update_attribute_values
|
13
|
+
return unless ThinkingSphinx.updates_enabled? && ThinkingSphinx.sphinx_running?
|
14
|
+
|
15
|
+
config = ThinkingSphinx::Configuration.instance
|
16
|
+
client = Riddle::Client.new config.address, config.port
|
17
|
+
|
18
|
+
self.sphinx_indexes.each do |index|
|
19
|
+
attribute_pairs = attribute_values_for_index(index)
|
20
|
+
attribute_names = attribute_pairs.keys
|
21
|
+
attribute_values = attribute_names.collect { |key|
|
22
|
+
attribute_pairs[key]
|
23
|
+
}
|
24
|
+
|
25
|
+
client.update "#{index.name}_core", attribute_names, {
|
26
|
+
sphinx_document_id => attribute_values
|
27
|
+
} if in_core_index?
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def updatable_attributes(index)
|
32
|
+
index.attributes.select { |attrib| attrib.updatable? }
|
33
|
+
end
|
34
|
+
|
35
|
+
def attribute_values_for_index(index)
|
36
|
+
updatable_attributes(index).inject({}) { |hash, attrib|
|
37
|
+
if attrib.type == :datetime
|
38
|
+
hash[attrib.unique_name.to_s] = attrib.live_value(self).to_time.to_i
|
39
|
+
else
|
40
|
+
hash[attrib.unique_name.to_s] = attrib.live_value self
|
41
|
+
end
|
42
|
+
|
43
|
+
hash
|
44
|
+
}
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -69,7 +69,16 @@ module ThinkingSphinx
|
|
69
69
|
end
|
70
70
|
|
71
71
|
def should_toggle_delta?
|
72
|
-
|
72
|
+
self.new_record? || indexed_data_changed?
|
73
|
+
end
|
74
|
+
|
75
|
+
def indexed_data_changed?
|
76
|
+
sphinx_indexes.any? { |index|
|
77
|
+
index.fields.any? { |field| field.changed?(self) } ||
|
78
|
+
index.attributes.any? { |attrib|
|
79
|
+
attrib.public? && attrib.changed?(self) && !attrib.updatable?
|
80
|
+
}
|
81
|
+
}
|
73
82
|
end
|
74
83
|
end
|
75
84
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'thinking_sphinx/active_record/attribute_updates'
|
1
2
|
require 'thinking_sphinx/active_record/delta'
|
2
3
|
require 'thinking_sphinx/active_record/search'
|
3
4
|
require 'thinking_sphinx/active_record/has_many_association'
|
@@ -65,7 +66,7 @@ module ThinkingSphinx
|
|
65
66
|
return unless ThinkingSphinx.define_indexes?
|
66
67
|
|
67
68
|
self.sphinx_indexes ||= []
|
68
|
-
index = Index.
|
69
|
+
index = ThinkingSphinx::Index::Builder.generate(self, &block)
|
69
70
|
|
70
71
|
self.sphinx_indexes << index
|
71
72
|
unless ThinkingSphinx.indexed_models.include?(self.name)
|
@@ -79,6 +80,8 @@ module ThinkingSphinx
|
|
79
80
|
|
80
81
|
after_destroy :toggle_deleted
|
81
82
|
|
83
|
+
include ThinkingSphinx::ActiveRecord::AttributeUpdates
|
84
|
+
|
82
85
|
index
|
83
86
|
end
|
84
87
|
alias_method :sphinx_index, :define_index
|
@@ -148,7 +151,9 @@ module ThinkingSphinx
|
|
148
151
|
self.sphinx_indexes.select { |ts_index|
|
149
152
|
ts_index.model == self
|
150
153
|
}.each_with_index do |ts_index, i|
|
151
|
-
index.sources
|
154
|
+
index.sources += ts_index.sources.collect { |source|
|
155
|
+
source.to_riddle_for_core(offset, i)
|
156
|
+
}
|
152
157
|
end
|
153
158
|
|
154
159
|
index
|
@@ -160,7 +165,9 @@ module ThinkingSphinx
|
|
160
165
|
index.path = File.join(ThinkingSphinx::Configuration.instance.searchd_file_path, index.name)
|
161
166
|
|
162
167
|
self.sphinx_indexes.each_with_index do |ts_index, i|
|
163
|
-
index.sources
|
168
|
+
index.sources += ts_index.sources.collect { |source|
|
169
|
+
source.to_riddle_for_delta(offset, i)
|
170
|
+
} if ts_index.delta?
|
164
171
|
end
|
165
172
|
|
166
173
|
index
|
@@ -11,7 +11,7 @@ module ThinkingSphinx
|
|
11
11
|
|
12
12
|
def concatenate(clause, separator = ' ')
|
13
13
|
clause.split(', ').collect { |field|
|
14
|
-
"COALESCE(CAST(#{field} as varchar), '')"
|
14
|
+
field[/COALESCE/] ? field : "COALESCE(CAST(#{field} as varchar), '')"
|
15
15
|
}.join(" || '#{separator}' || ")
|
16
16
|
end
|
17
17
|
|
@@ -8,8 +8,8 @@ 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.
|
@@ -67,20 +67,17 @@ module ThinkingSphinx
|
|
67
67
|
# If you're creating attributes for latitude and longitude, don't forget
|
68
68
|
# that Sphinx expects these values to be in radians.
|
69
69
|
#
|
70
|
-
def initialize(columns, options = {})
|
71
|
-
|
72
|
-
@associations = {}
|
70
|
+
def initialize(source, columns, options = {})
|
71
|
+
super
|
73
72
|
|
74
|
-
|
73
|
+
@type = options[:type]
|
74
|
+
@query_source = options[:source]
|
75
|
+
@crc = options[:crc]
|
75
76
|
|
76
|
-
@
|
77
|
-
@type
|
78
|
-
@faceted = options[:facet]
|
79
|
-
@source = options[:source]
|
80
|
-
@crc = options[:crc]
|
77
|
+
@type ||= :multi unless @query_source.nil?
|
78
|
+
@type = :integer if @type == :string && @crc
|
81
79
|
|
82
|
-
|
83
|
-
@type = :integer if @type == :string && @crc
|
80
|
+
source.attributes << self
|
84
81
|
end
|
85
82
|
|
86
83
|
# Get the part of the SELECT clause related to this attribute. Don't forget
|
@@ -93,37 +90,20 @@ module ThinkingSphinx
|
|
93
90
|
return nil unless include_as_association?
|
94
91
|
|
95
92
|
clause = @columns.collect { |column|
|
96
|
-
column_with_prefix(column)
|
93
|
+
part = column_with_prefix(column)
|
94
|
+
type == :string ? adapter.convert_nulls(part) : part
|
97
95
|
}.join(', ')
|
98
96
|
|
99
|
-
separator = all_ints? ? ',' : ' '
|
97
|
+
separator = all_ints? || @crc ? ',' : ' '
|
100
98
|
|
101
|
-
clause = adapter.concatenate(clause, separator) if concat_ws?
|
102
|
-
clause = adapter.group_concatenate(clause, separator) if is_many?
|
103
99
|
clause = adapter.cast_to_datetime(clause) if type == :datetime
|
104
|
-
clause = adapter.convert_nulls(clause) if type == :string
|
105
100
|
clause = adapter.crc(clause) if @crc
|
101
|
+
clause = adapter.concatenate(clause, separator) if concat_ws?
|
102
|
+
clause = adapter.group_concatenate(clause, separator) if is_many?
|
106
103
|
|
107
104
|
"#{clause} AS #{quote_column(unique_name)}"
|
108
105
|
end
|
109
106
|
|
110
|
-
# Get the part of the GROUP BY clause related to this attribute - if one is
|
111
|
-
# needed. If not, all you'll get back is nil. The latter will happen if
|
112
|
-
# there isn't actually a real column to get data from, or if there's
|
113
|
-
# multiple data values (read: a has_many or has_and_belongs_to_many
|
114
|
-
# association).
|
115
|
-
#
|
116
|
-
def to_group_sql
|
117
|
-
case
|
118
|
-
when is_many?, is_string?, ThinkingSphinx.use_group_by_shortcut?
|
119
|
-
nil
|
120
|
-
else
|
121
|
-
@columns.collect { |column|
|
122
|
-
column_with_prefix(column)
|
123
|
-
}
|
124
|
-
end
|
125
|
-
end
|
126
|
-
|
127
107
|
def type_to_config
|
128
108
|
{
|
129
109
|
:multi => :sql_attr_multi,
|
@@ -136,7 +116,7 @@ module ThinkingSphinx
|
|
136
116
|
end
|
137
117
|
|
138
118
|
def include_as_association?
|
139
|
-
! (type == :multi && (
|
119
|
+
! (type == :multi && (query_source == :query || query_source == :ranged_query))
|
140
120
|
end
|
141
121
|
|
142
122
|
# Returns the configuration value that should be used for
|
@@ -153,20 +133,7 @@ module ThinkingSphinx
|
|
153
133
|
unique_name
|
154
134
|
end
|
155
135
|
end
|
156
|
-
|
157
|
-
# Returns the unique name of the attribute - which is either the alias of
|
158
|
-
# the attribute, or the name of the only column - if there is only one. If
|
159
|
-
# there isn't, there should be an alias. Else things probably won't work.
|
160
|
-
# Consider yourself warned.
|
161
|
-
#
|
162
|
-
def unique_name
|
163
|
-
if @columns.length == 1
|
164
|
-
@alias || @columns.first.__name
|
165
|
-
else
|
166
|
-
@alias
|
167
|
-
end
|
168
|
-
end
|
169
|
-
|
136
|
+
|
170
137
|
# Returns the type of the column. If that's not already set, it returns
|
171
138
|
# :multi if there's the possibility of more than one value, :string if
|
172
139
|
# there's more than one association, otherwise it figures out what the
|
@@ -192,18 +159,34 @@ module ThinkingSphinx
|
|
192
159
|
end
|
193
160
|
end
|
194
161
|
|
195
|
-
def
|
196
|
-
|
197
|
-
|
198
|
-
|
162
|
+
def updatable?
|
163
|
+
[:integer, :datetime, :boolean].include?(type) && !is_string?
|
164
|
+
end
|
165
|
+
|
166
|
+
def live_value(instance)
|
167
|
+
object = instance
|
168
|
+
column = @columns.first
|
169
|
+
column.__stack.each { |method| object = object.send(method) }
|
170
|
+
object.send(column.__name)
|
171
|
+
end
|
172
|
+
|
173
|
+
def all_ints?
|
174
|
+
@columns.all? { |col|
|
175
|
+
klasses = @associations[col].empty? ? [@model] :
|
176
|
+
@associations[col].collect { |assoc| assoc.reflection.klass }
|
177
|
+
klasses.all? { |klass|
|
178
|
+
column = klass.columns.detect { |column| column.name == col.__name.to_s }
|
179
|
+
!column.nil? && column.type == :integer
|
180
|
+
}
|
181
|
+
}
|
199
182
|
end
|
200
183
|
|
201
184
|
private
|
202
185
|
|
203
186
|
def source_value(offset)
|
204
187
|
if is_string?
|
205
|
-
"#{
|
206
|
-
elsif
|
188
|
+
"#{query_source.to_s.dasherize}; #{columns.first.__name}"
|
189
|
+
elsif query_source == :ranged_query
|
207
190
|
"ranged-query; #{query offset} #{query_clause}; #{range_query}"
|
208
191
|
else
|
209
192
|
"query; #{query offset}"
|
@@ -249,84 +232,10 @@ FROM #{quote_table_name assoc.table}
|
|
249
232
|
}
|
250
233
|
end
|
251
234
|
|
252
|
-
def adapter
|
253
|
-
@adapter ||= @model.sphinx_database_adapter
|
254
|
-
end
|
255
|
-
|
256
|
-
def quote_with_table(table, column)
|
257
|
-
"#{quote_table_name(table)}.#{quote_column(column)}"
|
258
|
-
end
|
259
|
-
|
260
|
-
def quote_column(column)
|
261
|
-
@model.connection.quote_column_name(column)
|
262
|
-
end
|
263
|
-
|
264
|
-
def quote_table_name(table_name)
|
265
|
-
@model.connection.quote_table_name(table_name)
|
266
|
-
end
|
267
|
-
|
268
|
-
# Indication of whether the columns should be concatenated with a space
|
269
|
-
# between each value. True if there's either multiple sources or multiple
|
270
|
-
# associations.
|
271
|
-
#
|
272
|
-
def concat_ws?
|
273
|
-
multiple_associations? || @columns.length > 1
|
274
|
-
end
|
275
|
-
|
276
|
-
# Checks whether any column requires multiple associations (which only
|
277
|
-
# happens for polymorphic situations).
|
278
|
-
#
|
279
|
-
def multiple_associations?
|
280
|
-
associations.any? { |col,assocs| assocs.length > 1 }
|
281
|
-
end
|
282
|
-
|
283
|
-
# Builds a column reference tied to the appropriate associations. This
|
284
|
-
# dives into the associations hash and their corresponding joins to
|
285
|
-
# figure out how to correctly reference a column in SQL.
|
286
|
-
#
|
287
|
-
def column_with_prefix(column)
|
288
|
-
if column.is_string?
|
289
|
-
column.__name
|
290
|
-
elsif associations[column].empty?
|
291
|
-
"#{@model.quoted_table_name}.#{quote_column(column.__name)}"
|
292
|
-
else
|
293
|
-
associations[column].collect { |assoc|
|
294
|
-
assoc.has_column?(column.__name) ?
|
295
|
-
"#{quote_table_name(assoc.join.aliased_table_name)}" +
|
296
|
-
".#{quote_column(column.__name)}" :
|
297
|
-
nil
|
298
|
-
}.compact.join(', ')
|
299
|
-
end
|
300
|
-
end
|
301
|
-
|
302
|
-
# Could there be more than one value related to the parent record? If so,
|
303
|
-
# then this will return true. If not, false. It's that simple.
|
304
|
-
#
|
305
|
-
def is_many?
|
306
|
-
associations.values.flatten.any? { |assoc| assoc.is_many? }
|
307
|
-
end
|
308
|
-
|
309
235
|
def is_many_ints?
|
310
236
|
concat_ws? && all_ints?
|
311
237
|
end
|
312
|
-
|
313
|
-
# Returns true if any of the columns are string values, instead of database
|
314
|
-
# column references.
|
315
|
-
def is_string?
|
316
|
-
columns.all? { |col| col.is_string? }
|
317
|
-
end
|
318
|
-
|
319
|
-
def all_ints?
|
320
|
-
@columns.all? { |col|
|
321
|
-
klasses = @associations[col].empty? ? [@model] :
|
322
|
-
@associations[col].collect { |assoc| assoc.reflection.klass }
|
323
|
-
klasses.all? { |klass|
|
324
|
-
column = klass.columns.detect { |column| column.name == col.__name.to_s }
|
325
|
-
!column.nil? && column.type == :integer
|
326
|
-
}
|
327
|
-
}
|
328
|
-
end
|
329
|
-
|
238
|
+
|
330
239
|
def type_from_database
|
331
240
|
klass = @associations.values.flatten.first ?
|
332
241
|
@associations.values.flatten.first.reflection.klass : @model
|
@@ -347,9 +256,10 @@ FROM #{quote_table_name assoc.table}
|
|
347
256
|
else
|
348
257
|
raise <<-MESSAGE
|
349
258
|
|
350
|
-
Cannot automatically map
|
351
|
-
type (integer, float, boolean, datetime, string as ordinal).
|
352
|
-
explicitly convert the column's value in your define_index
|
259
|
+
Cannot automatically map attribute #{unique_name} in #{@model.name} to an
|
260
|
+
equivalent Sphinx type (integer, float, boolean, datetime, string as ordinal).
|
261
|
+
You could try to explicitly convert the column's value in your define_index
|
262
|
+
block:
|
353
263
|
has "CAST(column AS INT)", :type => :integer, :as => :column
|
354
264
|
MESSAGE
|
355
265
|
end
|
@@ -46,6 +46,7 @@ module ThinkingSphinx
|
|
46
46
|
ids = matches.collect { |match| match[:attributes]["sphinx_internal_id"] }
|
47
47
|
instances = ids.length > 0 ? klass.find(
|
48
48
|
:all,
|
49
|
+
:joins => options[:joins],
|
49
50
|
:conditions => {klass.primary_key.to_sym => ids},
|
50
51
|
:include => (options[:include] || index_options[:include]),
|
51
52
|
:select => (options[:select] || index_options[:select]),
|
@@ -31,8 +31,10 @@ module ThinkingSphinx
|
|
31
31
|
# fashion to database.yml - using the following keys: config_file,
|
32
32
|
# searchd_log_file, query_log_file, pid_file, searchd_file_path, port,
|
33
33
|
# allow_star, enable_star, min_prefix_len, min_infix_len, mem_limit,
|
34
|
-
# max_matches,
|
35
|
-
# html_strip,
|
34
|
+
# max_matches, morphology, charset_type, charset_table, ignore_chars,
|
35
|
+
# html_strip, html_remove_elements, delayed_job_priority.
|
36
|
+
#
|
37
|
+
# I think you've got the idea.
|
36
38
|
#
|
37
39
|
# Each setting in the YAML file is optional - so only put in the ones you
|
38
40
|
# want to change.
|
@@ -55,7 +57,8 @@ module ThinkingSphinx
|
|
55
57
|
|
56
58
|
attr_accessor :config_file, :searchd_log_file, :query_log_file,
|
57
59
|
:pid_file, :searchd_file_path, :address, :port, :allow_star,
|
58
|
-
:database_yml_file, :app_root, :bin_path, :model_directories
|
60
|
+
:database_yml_file, :app_root, :bin_path, :model_directories,
|
61
|
+
:delayed_job_priority
|
59
62
|
|
60
63
|
attr_accessor :source_options, :index_options
|
61
64
|
|
@@ -87,6 +90,7 @@ module ThinkingSphinx
|
|
87
90
|
self.bin_path = ""
|
88
91
|
self.model_directories = ["#{app_root}/app/models/"] +
|
89
92
|
Dir.glob("#{app_root}/vendor/plugins/*/app/models/")
|
93
|
+
self.delayed_job_priority = 0
|
90
94
|
|
91
95
|
self.source_options = {}
|
92
96
|
self.index_options = {
|
@@ -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
|
@@ -39,11 +39,12 @@ module ThinkingSphinx
|
|
39
39
|
|
40
40
|
def reset_query(model)
|
41
41
|
"UPDATE #{model.quoted_table_name} SET " +
|
42
|
-
"#{
|
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)}"
|
43
44
|
end
|
44
45
|
|
45
46
|
def clause(model, toggled)
|
46
|
-
"#{model.quoted_table_name}.#{
|
47
|
+
"#{model.quoted_table_name}.#{model.connection.quote_column_name(@column.to_s)}" +
|
47
48
|
" = #{adapter.boolean(toggled)}"
|
48
49
|
end
|
49
50
|
|
@@ -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
|