freelancing-god-thinking-sphinx 1.1.12 → 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.textile CHANGED
@@ -124,3 +124,4 @@ Since I first released this library, there's been quite a few people who have su
124
124
  * Mark Lane
125
125
  * Eric Lindvall
126
126
  * Lawrence Pit
127
+ * Mike Bailey
@@ -18,6 +18,7 @@ require 'thinking_sphinx/class_facet'
18
18
  require 'thinking_sphinx/facet_collection'
19
19
  require 'thinking_sphinx/field'
20
20
  require 'thinking_sphinx/index'
21
+ require 'thinking_sphinx/source'
21
22
  require 'thinking_sphinx/rails_additions'
22
23
  require 'thinking_sphinx/search'
23
24
  require 'thinking_sphinx/deltas'
@@ -36,7 +37,7 @@ module ThinkingSphinx
36
37
  module Version #:nodoc:
37
38
  Major = 1
38
39
  Minor = 1
39
- Tiny = 12
40
+ Tiny = 14
40
41
 
41
42
  String = [Major, Minor, Tiny].join('.')
42
43
  end
@@ -66,7 +66,7 @@ module ThinkingSphinx
66
66
  return unless ThinkingSphinx.define_indexes?
67
67
 
68
68
  self.sphinx_indexes ||= []
69
- index = Index.new(self, &block)
69
+ index = ThinkingSphinx::Index::Builder.generate(self, &block)
70
70
 
71
71
  self.sphinx_indexes << index
72
72
  unless ThinkingSphinx.indexed_models.include?(self.name)
@@ -151,7 +151,9 @@ module ThinkingSphinx
151
151
  self.sphinx_indexes.select { |ts_index|
152
152
  ts_index.model == self
153
153
  }.each_with_index do |ts_index, i|
154
- index.sources << ts_index.to_riddle_for_core(offset, i)
154
+ index.sources += ts_index.sources.collect { |source|
155
+ source.to_riddle_for_core(offset, i)
156
+ }
155
157
  end
156
158
 
157
159
  index
@@ -163,7 +165,9 @@ module ThinkingSphinx
163
165
  index.path = File.join(ThinkingSphinx::Configuration.instance.searchd_file_path, index.name)
164
166
 
165
167
  self.sphinx_indexes.each_with_index do |ts_index, i|
166
- index.sources << ts_index.to_riddle_for_delta(offset, i) if ts_index.delta?
168
+ index.sources += ts_index.sources.collect { |source|
169
+ source.to_riddle_for_delta(offset, i)
170
+ } if ts_index.delta?
167
171
  end
168
172
 
169
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
 
@@ -9,7 +9,7 @@ module ThinkingSphinx
9
9
  # associations. Which can get messy. Use Index.link!, it really helps.
10
10
  #
11
11
  class Attribute < ThinkingSphinx::Property
12
- attr_accessor :source
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,15 +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 = {})
70
+ def initialize(source, columns, options = {})
71
71
  super
72
72
 
73
- @type = options[:type]
74
- @source = options[:source]
75
- @crc = options[:crc]
73
+ @type = options[:type]
74
+ @query_source = options[:source]
75
+ @crc = options[:crc]
76
76
 
77
- @type ||= :multi unless @source.nil?
78
- @type = :integer if @type == :string && @crc
77
+ @type ||= :multi unless @query_source.nil?
78
+ @type = :integer if @type == :string && @crc
79
+
80
+ source.attributes << self
79
81
  end
80
82
 
81
83
  # Get the part of the SELECT clause related to this attribute. Don't forget
@@ -88,16 +90,16 @@ module ThinkingSphinx
88
90
  return nil unless include_as_association?
89
91
 
90
92
  clause = @columns.collect { |column|
91
- column_with_prefix(column)
93
+ part = column_with_prefix(column)
94
+ type == :string ? adapter.convert_nulls(part) : part
92
95
  }.join(', ')
93
96
 
94
- separator = all_ints? ? ',' : ' '
97
+ separator = all_ints? || @crc ? ',' : ' '
95
98
 
96
- clause = adapter.concatenate(clause, separator) if concat_ws?
97
- clause = adapter.group_concatenate(clause, separator) if is_many?
98
99
  clause = adapter.cast_to_datetime(clause) if type == :datetime
99
- clause = adapter.convert_nulls(clause) if type == :string
100
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?
101
103
 
102
104
  "#{clause} AS #{quote_column(unique_name)}"
103
105
  end
@@ -114,7 +116,7 @@ module ThinkingSphinx
114
116
  end
115
117
 
116
118
  def include_as_association?
117
- ! (type == :multi && (source == :query || source == :ranged_query))
119
+ ! (type == :multi && (query_source == :query || query_source == :ranged_query))
118
120
  end
119
121
 
120
122
  # Returns the configuration value that should be used for
@@ -168,12 +170,23 @@ module ThinkingSphinx
168
170
  object.send(column.__name)
169
171
  end
170
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
+ }
182
+ end
183
+
171
184
  private
172
185
 
173
186
  def source_value(offset)
174
187
  if is_string?
175
- "#{source.to_s.dasherize}; #{columns.first.__name}"
176
- elsif source == :ranged_query
188
+ "#{query_source.to_s.dasherize}; #{columns.first.__name}"
189
+ elsif query_source == :ranged_query
177
190
  "ranged-query; #{query offset} #{query_clause}; #{range_query}"
178
191
  else
179
192
  "query; #{query offset}"
@@ -223,17 +236,6 @@ FROM #{quote_table_name assoc.table}
223
236
  concat_ws? && all_ints?
224
237
  end
225
238
 
226
- def all_ints?
227
- @columns.all? { |col|
228
- klasses = @associations[col].empty? ? [@model] :
229
- @associations[col].collect { |assoc| assoc.reflection.klass }
230
- klasses.all? { |klass|
231
- column = klass.columns.detect { |column| column.name == col.__name.to_s }
232
- !column.nil? && column.type == :integer
233
- }
234
- }
235
- end
236
-
237
239
  def type_from_database
238
240
  klass = @associations.values.flatten.first ?
239
241
  @associations.values.flatten.first.reflection.klass : @model
@@ -254,9 +256,10 @@ FROM #{quote_table_name assoc.table}
254
256
  else
255
257
  raise <<-MESSAGE
256
258
 
257
- Cannot automatically map column type #{type_from_db} to an equivalent Sphinx
258
- type (integer, float, boolean, datetime, string as ordinal). You could try to
259
- explicitly convert the column's value in your define_index block:
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:
260
263
  has "CAST(column AS INT)", :type => :integer, :as => :column
261
264
  MESSAGE
262
265
  end
@@ -4,20 +4,23 @@ require 'thinking_sphinx/deltas/datetime_delta'
4
4
 
5
5
  module ThinkingSphinx
6
6
  module Deltas
7
- def self.parse(index, options)
8
- delta_option = options.delete(:delta)
7
+ def self.parse(index)
8
+ delta_option = index.local_options.delete(:delta)
9
9
  case delta_option
10
10
  when TrueClass, :default
11
- DefaultDelta.new index, options
11
+ DefaultDelta.new index, index.local_options
12
12
  when :delayed
13
- DelayedDelta.new index, options
13
+ DelayedDelta.new index, index.local_options
14
14
  when :datetime
15
- DatetimeDelta.new index, options
15
+ DatetimeDelta.new index, index.local_options
16
16
  when FalseClass, nil
17
17
  nil
18
18
  else
19
+ if delta_option.is_a?(String)
20
+ delta_option = Kernel.const_get(delta_option)
21
+ end
19
22
  if delta_option.ancestors.include?(ThinkingSphinx::Deltas::DefaultDelta)
20
- delta_option.new index, options
23
+ delta_option.new index, index.local_options
21
24
  else
22
25
  raise "Unknown delta type"
23
26
  end
@@ -39,7 +39,7 @@ module ThinkingSphinx
39
39
 
40
40
  def clause(model, toggled)
41
41
  if toggled
42
- "#{model.quoted_table_name}.#{@index.quote_column(@column.to_s)}" +
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,12 +39,12 @@ module ThinkingSphinx
39
39
 
40
40
  def reset_query(model)
41
41
  "UPDATE #{model.quoted_table_name} SET " +
42
- "#{@index.quote_column(@column.to_s)} = #{adapter.boolean(false)} " +
43
- "WHERE #{@index.quote_column(@column.to_s)} = #{adapter.boolean(true)}"
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)}"
44
44
  end
45
45
 
46
46
  def clause(model, toggled)
47
- "#{model.quoted_table_name}.#{@index.quote_column(@column.to_s)}" +
47
+ "#{model.quoted_table_name}.#{model.connection.quote_column_name(@column.to_s)}" +
48
48
  " = #{adapter.boolean(toggled)}"
49
49
  end
50
50
 
@@ -1,80 +1,82 @@
1
- namespace :thinking_sphinx do
2
- namespace :install do
3
- desc "Install Sphinx by source"
4
- task :sphinx do
5
- with_postgres = false
6
- run "which pg_config" do |channel, stream, data|
7
- with_postgres = !(data.nil? || data == "")
8
- end
1
+ Capistrano::Configuration.instance(:must_exist).load do
2
+ namespace :thinking_sphinx do
3
+ namespace :install do
4
+ desc "Install Sphinx by source"
5
+ task :sphinx do
6
+ with_postgres = false
7
+ run "which pg_config" do |channel, stream, data|
8
+ with_postgres = !(data.nil? || data == "")
9
+ end
9
10
 
10
- args = []
11
- if with_postgres
12
- run "pg_config --pkgincludedir" do |channel, stream, data|
13
- args << "--with-pgsql=#{data}"
11
+ args = []
12
+ if with_postgres
13
+ run "pg_config --pkgincludedir" do |channel, stream, data|
14
+ args << "--with-pgsql=#{data}"
15
+ end
14
16
  end
15
- end
16
17
 
17
- commands = <<-CMD
18
- wget -q http://www.sphinxsearch.com/downloads/sphinx-0.9.8.1.tar.gz >> sphinx.log
19
- tar xzvf sphinx-0.9.8.1.tar.gz
20
- cd sphinx-0.9.8.1
21
- ./configure #{args.join(" ")}
22
- make
23
- sudo make install
24
- rm -rf sphinx-0.9.8.1 sphinx-0.9.8.1.tar.gz
25
- CMD
26
- run commands.split(/\n\s+/).join(" && ")
27
- end
18
+ commands = <<-CMD
19
+ wget -q http://www.sphinxsearch.com/downloads/sphinx-0.9.8.1.tar.gz >> sphinx.log
20
+ tar xzvf sphinx-0.9.8.1.tar.gz
21
+ cd sphinx-0.9.8.1
22
+ ./configure #{args.join(" ")}
23
+ make
24
+ sudo make install
25
+ rm -rf sphinx-0.9.8.1 sphinx-0.9.8.1.tar.gz
26
+ CMD
27
+ run commands.split(/\n\s+/).join(" && ")
28
+ end
28
29
 
29
- desc "Install Thinking Sphinx as a gem from GitHub"
30
- task :ts do
31
- sudo "gem install freelancing-god-thinking-sphinx --source http://gems.github.com"
30
+ desc "Install Thinking Sphinx as a gem from GitHub"
31
+ task :ts do
32
+ sudo "gem install freelancing-god-thinking-sphinx --source http://gems.github.com"
33
+ end
32
34
  end
33
- end
34
35
 
35
- desc "Generate the Sphinx configuration file"
36
- task :configure do
37
- rake "thinking_sphinx:configure"
38
- end
36
+ desc "Generate the Sphinx configuration file"
37
+ task :configure do
38
+ rake "thinking_sphinx:configure"
39
+ end
39
40
 
40
- desc "Index data"
41
- task :index do
42
- rake "thinking_sphinx:index"
43
- end
41
+ desc "Index data"
42
+ task :index do
43
+ rake "thinking_sphinx:index"
44
+ end
44
45
 
45
- desc "Start the Sphinx daemon"
46
- task :start do
47
- configure
48
- rake "thinking_sphinx:start"
49
- end
46
+ desc "Start the Sphinx daemon"
47
+ task :start do
48
+ configure
49
+ rake "thinking_sphinx:start"
50
+ end
50
51
 
51
- desc "Stop the Sphinx daemon"
52
- task :stop do
53
- configure
54
- rake "thinking_sphinx:stop"
55
- end
52
+ desc "Stop the Sphinx daemon"
53
+ task :stop do
54
+ configure
55
+ rake "thinking_sphinx:stop"
56
+ end
56
57
 
57
- desc "Stop and then start the Sphinx daemon"
58
- task :restart do
59
- stop
60
- start
61
- end
58
+ desc "Stop and then start the Sphinx daemon"
59
+ task :restart do
60
+ stop
61
+ start
62
+ end
62
63
 
63
- desc "Stop, re-index and then start the Sphinx daemon"
64
- task :rebuild do
65
- stop
66
- index
67
- start
68
- end
64
+ desc "Stop, re-index and then start the Sphinx daemon"
65
+ task :rebuild do
66
+ stop
67
+ index
68
+ start
69
+ end
69
70
 
70
- desc "Add the shared folder for sphinx files for the production environment"
71
- task :shared_sphinx_folder, :roles => :web do
72
- sudo "mkdir -p #{shared_path}/db/sphinx/production"
73
- end
71
+ desc "Add the shared folder for sphinx files for the production environment"
72
+ task :shared_sphinx_folder, :roles => :web do
73
+ sudo "mkdir -p #{shared_path}/db/sphinx/production"
74
+ end
74
75
 
75
- def rake(*tasks)
76
- tasks.each do |t|
77
- run "cd #{current_path} && rake #{t} RAILS_ENV=production"
76
+ def rake(*tasks)
77
+ tasks.each do |t|
78
+ run "cd #{current_path} && rake #{t} RAILS_ENV=production"
79
+ end
78
80
  end
79
81
  end
80
82
  end
@@ -1,11 +1,11 @@
1
1
  module ThinkingSphinx
2
2
  class Facet
3
- attr_reader :reference
3
+ attr_reader :property
4
4
 
5
- def initialize(reference)
6
- @reference = reference
5
+ def initialize(property)
6
+ @property = property
7
7
 
8
- if reference.columns.length != 1
8
+ if property.columns.length != 1
9
9
  raise "Can't translate Facets on multiple-column field or attribute"
10
10
  end
11
11
  end
@@ -19,29 +19,62 @@ module ThinkingSphinx
19
19
  end
20
20
  end
21
21
 
22
- def name
23
- reference.unique_name
24
- end
25
-
26
22
  def self.attribute_name_for(name)
27
23
  name.to_s == 'class' ? 'class_crc' : "#{name}_facet"
28
24
  end
29
25
 
26
+ def self.attribute_name_from_value(name, value)
27
+ case value
28
+ when String
29
+ attribute_name_for(name)
30
+ when Array
31
+ if value.all? { |val| val.is_a?(Integer) }
32
+ name
33
+ else
34
+ attribute_name_for(name)
35
+ end
36
+ else
37
+ name
38
+ end
39
+ end
40
+
41
+ def self.translate?(property)
42
+ return true if property.is_a?(Field)
43
+
44
+ case property.type
45
+ when :string
46
+ true
47
+ when :integer, :boolean, :datetime, :float
48
+ false
49
+ when :multi
50
+ !property.all_ints?
51
+ end
52
+ end
53
+
54
+ def name
55
+ property.unique_name
56
+ end
57
+
30
58
  def attribute_name
31
- # @attribute_name ||= case @reference
32
- # when Attribute
33
- # @reference.unique_name.to_s
34
- # when Field
35
- @attribute_name ||= @reference.unique_name.to_s + "_facet"
36
- # end
59
+ if translate?
60
+ Facet.attribute_name_for(@property.unique_name)
61
+ else
62
+ @property.unique_name.to_s
63
+ end
64
+ end
65
+
66
+ def translate?
67
+ Facet.translate?(@property)
68
+ end
69
+
70
+ def type
71
+ @property.is_a?(Field) ? :string : @property.type
37
72
  end
38
73
 
39
74
  def value(object, attribute_value)
40
- return translate(object, attribute_value) if @reference.is_a?(Field)
75
+ return translate(object, attribute_value) if translate?
41
76
 
42
- case @reference.type
43
- when :string
44
- translate(object, attribute_value)
77
+ case @property.type
45
78
  when :datetime
46
79
  Time.at(attribute_value)
47
80
  when :boolean
@@ -61,11 +94,15 @@ module ThinkingSphinx
61
94
  column.__stack.each { |method|
62
95
  object = object.send(method)
63
96
  }
64
- object.send(column.__name)
97
+ if object.is_a?(Array)
98
+ object.collect { |item| item.send(column.__name) }
99
+ else
100
+ object.send(column.__name)
101
+ end
65
102
  end
66
103
 
67
104
  def column
68
- @reference.columns.first
105
+ @property.columns.first
69
106
  end
70
107
  end
71
108
  end