freelancing-god-thinking-sphinx 1.1.12 → 1.1.14

Sign up to get free protection for your applications and to get access to all the features.
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