warp-thinking-sphinx 1.2.12 → 1.3.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (144) hide show
  1. data/README.textile +21 -4
  2. data/VERSION +1 -0
  3. data/features/abstract_inheritance.feature +10 -0
  4. data/features/alternate_primary_key.feature +1 -1
  5. data/features/attribute_updates.feature +22 -5
  6. data/features/deleting_instances.feature +3 -0
  7. data/features/facets.feature +6 -0
  8. data/features/facets_across_model.feature +2 -2
  9. data/features/searching_across_models.feature +1 -1
  10. data/features/searching_by_index.feature +40 -0
  11. data/features/sphinx_scopes.feature +7 -0
  12. data/features/step_definitions/alpha_steps.rb +14 -1
  13. data/features/step_definitions/beta_steps.rb +1 -1
  14. data/features/step_definitions/common_steps.rb +12 -2
  15. data/features/step_definitions/facet_steps.rb +5 -1
  16. data/features/step_definitions/scope_steps.rb +4 -0
  17. data/features/step_definitions/sphinx_steps.rb +8 -4
  18. data/features/sti_searching.feature +5 -0
  19. data/features/support/{db/database.example.yml → database.example.yml} +0 -0
  20. data/features/support/db/fixtures/foxes.rb +3 -0
  21. data/features/support/db/fixtures/music.rb +4 -0
  22. data/features/support/db/fixtures/robots.rb +1 -1
  23. data/features/support/db/fixtures/tags.rb +1 -1
  24. data/features/support/db/migrations/create_alphas.rb +1 -0
  25. data/features/support/db/migrations/create_genres.rb +3 -0
  26. data/features/support/db/migrations/create_music.rb +6 -0
  27. data/features/support/db/migrations/create_robots.rb +1 -2
  28. data/features/support/env.rb +16 -1
  29. data/features/support/models/alpha.rb +12 -0
  30. data/features/support/models/comment.rb +3 -3
  31. data/features/support/models/fox.rb +5 -0
  32. data/features/support/models/genre.rb +3 -0
  33. data/features/support/models/medium.rb +5 -0
  34. data/features/support/models/music.rb +8 -0
  35. data/features/support/models/post.rb +2 -1
  36. data/features/support/models/robot.rb +4 -0
  37. data/lib/cucumber/thinking_sphinx/external_world.rb +8 -0
  38. data/lib/cucumber/thinking_sphinx/internal_world.rb +126 -0
  39. data/lib/cucumber/thinking_sphinx/sql_logger.rb +20 -0
  40. data/lib/thinking_sphinx.rb +56 -37
  41. data/lib/thinking_sphinx/active_record.rb +257 -192
  42. data/lib/thinking_sphinx/active_record/attribute_updates.rb +10 -12
  43. data/lib/thinking_sphinx/active_record/delta.rb +0 -26
  44. data/lib/thinking_sphinx/active_record/scopes.rb +37 -1
  45. data/lib/thinking_sphinx/adapters/mysql_adapter.rb +1 -1
  46. data/lib/thinking_sphinx/adapters/postgresql_adapter.rb +18 -11
  47. data/lib/thinking_sphinx/attribute.rb +19 -4
  48. data/lib/thinking_sphinx/auto_version.rb +22 -0
  49. data/lib/thinking_sphinx/configuration.rb +57 -59
  50. data/lib/thinking_sphinx/context.rb +74 -0
  51. data/lib/thinking_sphinx/deltas.rb +0 -2
  52. data/lib/thinking_sphinx/deltas/default_delta.rb +14 -20
  53. data/lib/thinking_sphinx/deploy/capistrano.rb +1 -1
  54. data/lib/thinking_sphinx/facet_search.rb +3 -1
  55. data/lib/thinking_sphinx/index.rb +77 -19
  56. data/lib/thinking_sphinx/index/builder.rb +2 -2
  57. data/lib/thinking_sphinx/search.rb +47 -9
  58. data/lib/thinking_sphinx/search_methods.rb +22 -4
  59. data/lib/thinking_sphinx/source.rb +9 -8
  60. data/lib/thinking_sphinx/source/sql.rb +5 -3
  61. data/lib/thinking_sphinx/tasks.rb +13 -57
  62. data/lib/thinking_sphinx/test.rb +52 -0
  63. data/rails/init.rb +4 -2
  64. data/spec/{lib/thinking_sphinx → thinking_sphinx}/active_record/delta_spec.rb +4 -6
  65. data/spec/{lib/thinking_sphinx → thinking_sphinx}/active_record/has_many_association_spec.rb +0 -0
  66. data/spec/thinking_sphinx/active_record/scopes_spec.rb +177 -0
  67. data/spec/thinking_sphinx/active_record_spec.rb +622 -0
  68. data/spec/{lib/thinking_sphinx → thinking_sphinx}/association_spec.rb +0 -0
  69. data/spec/{lib/thinking_sphinx → thinking_sphinx}/attribute_spec.rb +39 -0
  70. data/spec/thinking_sphinx/auto_version_spec.rb +39 -0
  71. data/spec/{lib/thinking_sphinx → thinking_sphinx}/configuration_spec.rb +27 -61
  72. data/spec/thinking_sphinx/context_spec.rb +119 -0
  73. data/spec/{lib/thinking_sphinx → thinking_sphinx}/core/array_spec.rb +0 -0
  74. data/spec/{lib/thinking_sphinx → thinking_sphinx}/core/string_spec.rb +0 -0
  75. data/spec/{lib/thinking_sphinx → thinking_sphinx}/excerpter_spec.rb +0 -0
  76. data/spec/{lib/thinking_sphinx → thinking_sphinx}/facet_search_spec.rb +0 -0
  77. data/spec/{lib/thinking_sphinx → thinking_sphinx}/facet_spec.rb +0 -0
  78. data/spec/{lib/thinking_sphinx → thinking_sphinx}/field_spec.rb +0 -0
  79. data/spec/{lib/thinking_sphinx → thinking_sphinx}/index/builder_spec.rb +24 -0
  80. data/spec/{lib/thinking_sphinx → thinking_sphinx}/index/faux_column_spec.rb +0 -0
  81. data/spec/thinking_sphinx/index_spec.rb +183 -0
  82. data/spec/{lib/thinking_sphinx → thinking_sphinx}/rails_additions_spec.rb +0 -0
  83. data/spec/{lib/thinking_sphinx → thinking_sphinx}/search_methods_spec.rb +0 -0
  84. data/spec/{lib/thinking_sphinx → thinking_sphinx}/search_spec.rb +41 -0
  85. data/spec/{lib/thinking_sphinx → thinking_sphinx}/source_spec.rb +1 -1
  86. data/spec/thinking_sphinx_spec.rb +204 -0
  87. data/tasks/distribution.rb +6 -20
  88. data/tasks/testing.rb +8 -19
  89. metadata +117 -142
  90. data/VERSION.yml +0 -4
  91. data/features/a.rb +0 -17
  92. data/features/datetime_deltas.feature +0 -66
  93. data/features/delayed_delta_indexing.feature +0 -37
  94. data/features/step_definitions/datetime_delta_steps.rb +0 -15
  95. data/features/step_definitions/delayed_delta_indexing_steps.rb +0 -7
  96. data/features/support/db/active_record.rb +0 -40
  97. data/features/support/db/fixtures/delayed_betas.rb +0 -10
  98. data/features/support/db/fixtures/thetas.rb +0 -10
  99. data/features/support/db/migrations/create_delayed_betas.rb +0 -17
  100. data/features/support/db/migrations/create_thetas.rb +0 -5
  101. data/features/support/db/mysql.rb +0 -3
  102. data/features/support/db/postgresql.rb +0 -3
  103. data/features/support/models/delayed_beta.rb +0 -7
  104. data/features/support/models/theta.rb +0 -7
  105. data/features/support/post_database.rb +0 -43
  106. data/features/support/z.rb +0 -19
  107. data/lib/thinking_sphinx/deltas/datetime_delta.rb +0 -50
  108. data/lib/thinking_sphinx/deltas/delayed_delta.rb +0 -30
  109. data/lib/thinking_sphinx/deltas/delayed_delta/delta_job.rb +0 -24
  110. data/lib/thinking_sphinx/deltas/delayed_delta/flag_as_deleted_job.rb +0 -27
  111. data/lib/thinking_sphinx/deltas/delayed_delta/job.rb +0 -26
  112. data/spec/lib/thinking_sphinx/active_record/scopes_spec.rb +0 -96
  113. data/spec/lib/thinking_sphinx/active_record_spec.rb +0 -353
  114. data/spec/lib/thinking_sphinx/deltas/job_spec.rb +0 -32
  115. data/spec/lib/thinking_sphinx/index_spec.rb +0 -45
  116. data/spec/lib/thinking_sphinx_spec.rb +0 -162
  117. data/vendor/after_commit/LICENSE +0 -20
  118. data/vendor/after_commit/README +0 -16
  119. data/vendor/after_commit/Rakefile +0 -22
  120. data/vendor/after_commit/init.rb +0 -8
  121. data/vendor/after_commit/lib/after_commit.rb +0 -45
  122. data/vendor/after_commit/lib/after_commit/active_record.rb +0 -114
  123. data/vendor/after_commit/lib/after_commit/connection_adapters.rb +0 -103
  124. data/vendor/after_commit/test/after_commit_test.rb +0 -53
  125. data/vendor/delayed_job/lib/delayed/job.rb +0 -251
  126. data/vendor/delayed_job/lib/delayed/message_sending.rb +0 -7
  127. data/vendor/delayed_job/lib/delayed/performable_method.rb +0 -55
  128. data/vendor/delayed_job/lib/delayed/worker.rb +0 -54
  129. data/vendor/riddle/lib/riddle.rb +0 -30
  130. data/vendor/riddle/lib/riddle/client.rb +0 -635
  131. data/vendor/riddle/lib/riddle/client/filter.rb +0 -53
  132. data/vendor/riddle/lib/riddle/client/message.rb +0 -66
  133. data/vendor/riddle/lib/riddle/client/response.rb +0 -84
  134. data/vendor/riddle/lib/riddle/configuration.rb +0 -33
  135. data/vendor/riddle/lib/riddle/configuration/distributed_index.rb +0 -48
  136. data/vendor/riddle/lib/riddle/configuration/index.rb +0 -142
  137. data/vendor/riddle/lib/riddle/configuration/indexer.rb +0 -19
  138. data/vendor/riddle/lib/riddle/configuration/remote_index.rb +0 -17
  139. data/vendor/riddle/lib/riddle/configuration/searchd.rb +0 -25
  140. data/vendor/riddle/lib/riddle/configuration/section.rb +0 -43
  141. data/vendor/riddle/lib/riddle/configuration/source.rb +0 -23
  142. data/vendor/riddle/lib/riddle/configuration/sql_source.rb +0 -34
  143. data/vendor/riddle/lib/riddle/configuration/xml_source.rb +0 -28
  144. data/vendor/riddle/lib/riddle/controller.rb +0 -53
@@ -0,0 +1,74 @@
1
+ class ThinkingSphinx::Context
2
+ attr_reader :indexed_models
3
+
4
+ def initialize
5
+ @indexed_models = []
6
+ end
7
+
8
+ def prepare
9
+ load_models
10
+ add_indexed_models if cached?
11
+ end
12
+
13
+ def define_indexes
14
+ indexed_models.each { |model|
15
+ model.constantize.define_indexes
16
+ }
17
+ end
18
+
19
+ def add_indexed_model(model)
20
+ model = model.name if model.is_a?(Class)
21
+
22
+ indexed_models << model
23
+ indexed_models.uniq!
24
+ indexed_models.sort!
25
+ end
26
+
27
+ def superclass_indexed_models
28
+ klasses = indexed_models.collect { |name| name.constantize }
29
+ klasses.reject { |klass|
30
+ klass.superclass.ancestors.any? { |ancestor| klasses.include?(ancestor) }
31
+ }.collect { |klass| klass.name }
32
+ end
33
+
34
+ private
35
+
36
+ def cached?
37
+ defined?(Rails) &&
38
+ Rails::VERSION::STRING.to_f > 2.1 &&
39
+ Rails.configuration.cache_classes
40
+ end
41
+
42
+ def add_indexed_models
43
+ Object.subclasses_of(ActiveRecord::Base).each do |klass|
44
+ add_indexed_model klass if klass.has_sphinx_indexes?
45
+ end
46
+ end
47
+
48
+ # Make sure all models are loaded - without reloading any that
49
+ # ActiveRecord::Base is already aware of (otherwise we start to hit some
50
+ # messy dependencies issues).
51
+ #
52
+ def load_models
53
+ ThinkingSphinx::Configuration.instance.model_directories.each do |base|
54
+ Dir["#{base}**/*.rb"].each do |file|
55
+ model_name = file.gsub(/^#{base}([\w_\/\\]+)\.rb/, '\1')
56
+
57
+ next if model_name.nil?
58
+ next if ::ActiveRecord::Base.send(:subclasses).detect { |model|
59
+ model.name == model_name
60
+ }
61
+
62
+ begin
63
+ model_name.camelize.constantize
64
+ rescue LoadError
65
+ model_name.gsub!(/.*[\/\\]/, '').nil? ? next : retry
66
+ rescue NameError
67
+ next
68
+ rescue StandardError
69
+ STDERR.puts "Warning: Error loading #{file}"
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -1,6 +1,4 @@
1
1
  require 'thinking_sphinx/deltas/default_delta'
2
- require 'thinking_sphinx/deltas/delayed_delta'
3
- require 'thinking_sphinx/deltas/datetime_delta'
4
2
 
5
3
  module ThinkingSphinx
6
4
  module Deltas
@@ -13,18 +13,8 @@ module ThinkingSphinx
13
13
  ThinkingSphinx.deltas_enabled?
14
14
  return true if instance && !toggled(instance)
15
15
 
16
- config = ThinkingSphinx::Configuration.instance
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?
22
-
23
- client.update(
24
- core_index_name(model),
25
- ['sphinx_deleted'],
26
- {instance.sphinx_document_id => [1]}
27
- ) if instance && ThinkingSphinx.sphinx_running? && instance.in_both_indexes?
16
+ update_delta_indexes model
17
+ delete_from_core model, instance if instance
28
18
 
29
19
  true
30
20
  end
@@ -48,17 +38,21 @@ module ThinkingSphinx
48
38
  " = #{adapter.boolean(toggled)}"
49
39
  end
50
40
 
51
- protected
41
+ private
52
42
 
53
- def core_index_name(model)
54
- "#{model.source_of_sphinx_index.name.underscore.tr(':/\\', '_')}_core"
43
+ def update_delta_indexes(model)
44
+ config = ThinkingSphinx::Configuration.instance
45
+ rotate = ThinkingSphinx.sphinx_running? ? "--rotate" : ""
46
+
47
+ output = `#{config.bin_path}#{config.indexer_binary_name} --config #{config.config_file} #{rotate} #{model.delta_index_names.join(' ')}`
48
+ puts(output) unless ThinkingSphinx.suppress_delta_output?
55
49
  end
56
50
 
57
- def delta_index_name(model)
58
- "#{model.source_of_sphinx_index.name.underscore.tr(':/\\', '_')}_delta"
59
- end
60
-
61
- private
51
+ def delete_from_core(model, instance)
52
+ model.core_index_names.each do |index_name|
53
+ model.delete_in_index index_name, instance.sphinx_document_id
54
+ end
55
+ end
62
56
 
63
57
  def adapter
64
58
  @adapter = @index.model.sphinx_database_adapter
@@ -93,7 +93,7 @@ DESC
93
93
  rails_env = fetch(:rails_env, "production")
94
94
  rake = fetch(:rake, "rake")
95
95
  tasks.each do |t|
96
- run "cd #{current_path}; #{rake} RAILS_ENV=#{rails_env} #{t}"
96
+ run "if [ -d #{release_path} ]; then cd #{release_path}; else cd #{current_path}; fi; #{rake} RAILS_ENV=#{rails_env} #{t}"
97
97
  end
98
98
  end
99
99
  end
@@ -3,6 +3,8 @@ module ThinkingSphinx
3
3
  attr_accessor :args, :options
4
4
 
5
5
  def initialize(*args)
6
+ ThinkingSphinx.context.define_indexes
7
+
6
8
  @options = args.extract_options!
7
9
  @args = args
8
10
 
@@ -64,7 +66,7 @@ module ThinkingSphinx
64
66
 
65
67
  def facet_classes
66
68
  (
67
- options[:classes] || ThinkingSphinx.indexed_models.collect { |model|
69
+ options[:classes] || ThinkingSphinx.context.indexed_models.collect { |model|
68
70
  model.constantize
69
71
  }
70
72
  ).select { |klass| klass.sphinx_facets.any? }
@@ -2,14 +2,8 @@ require 'thinking_sphinx/index/builder'
2
2
  require 'thinking_sphinx/index/faux_column'
3
3
 
4
4
  module ThinkingSphinx
5
- # The Index class is a ruby representation of a Sphinx source (not a Sphinx
6
- # index - yes, I know it's a little confusing. You'll manage). This is
7
- # another 'internal' Thinking Sphinx class - if you're using it directly,
8
- # you either know what you're doing, or messing with things beyond your ken.
9
- # Enjoy.
10
- #
11
5
  class Index
12
- attr_accessor :model, :sources, :delta_object
6
+ attr_accessor :name, :model, :sources, :delta_object
13
7
 
14
8
  # Create a new index instance by passing in the model it is tied to, and
15
9
  # a block to build it with (optional but recommended). For documentation
@@ -26,6 +20,7 @@ module ThinkingSphinx
26
20
  # end
27
21
  #
28
22
  def initialize(model, &block)
23
+ @name = self.class.name_for model
29
24
  @model = model
30
25
  @sources = []
31
26
  @options = {}
@@ -40,8 +35,19 @@ module ThinkingSphinx
40
35
  @sources.collect { |source| source.attributes }.flatten
41
36
  end
42
37
 
43
- def name
44
- self.class.name_for @model
38
+ def core_name
39
+ "#{name}_core"
40
+ end
41
+
42
+ def delta_name
43
+ "#{name}_delta"
44
+ end
45
+
46
+ def all_names
47
+ names = [core_name]
48
+ names << delta_name if delta?
49
+
50
+ names
45
51
  end
46
52
 
47
53
  def self.name_for(model)
@@ -61,7 +67,7 @@ module ThinkingSphinx
61
67
  end
62
68
 
63
69
  def options
64
- all_index_options = ThinkingSphinx::Configuration.instance.index_options.clone
70
+ all_index_options = config.index_options.clone
65
71
  @options.keys.select { |key|
66
72
  ThinkingSphinx::Configuration::IndexOptions.include?(key.to_s) ||
67
73
  ThinkingSphinx::Configuration::CustomOptions.include?(key.to_s)
@@ -73,6 +79,12 @@ module ThinkingSphinx
73
79
  !@delta_object.nil?
74
80
  end
75
81
 
82
+ def to_riddle(offset)
83
+ indexes = [to_riddle_for_core(offset)]
84
+ indexes << to_riddle_for_delta(offset) if delta?
85
+ indexes << to_riddle_for_distributed
86
+ end
87
+
76
88
  private
77
89
 
78
90
  def adapter
@@ -83,17 +95,63 @@ module ThinkingSphinx
83
95
  options[:charset_type] == "utf-8"
84
96
  end
85
97
 
86
- # Does all the magic with the block provided to the base #initialize.
87
- # Creates a new class subclassed from Builder, and evaluates the block
88
- # on it, then pulls all relevant settings - fields, attributes, conditions,
89
- # properties - into the new index.
90
- #
91
- def initialize_from_builder(&block)
92
- #
93
- end
94
-
95
98
  def sql_query_pre_for_delta
96
99
  [""]
97
100
  end
101
+
102
+ def config
103
+ @config ||= ThinkingSphinx::Configuration.instance
104
+ end
105
+
106
+ def to_riddle_for_core(offset)
107
+ index = Riddle::Configuration::Index.new core_name
108
+ index.path = File.join config.searchd_file_path, index.name
109
+
110
+ set_configuration_options_for_indexes index
111
+ set_field_settings_for_indexes index
112
+
113
+ sources.each_with_index do |source, i|
114
+ index.sources << source.to_riddle_for_core(offset, i)
115
+ end
116
+
117
+ index
118
+ end
119
+
120
+ def to_riddle_for_delta(offset)
121
+ index = Riddle::Configuration::Index.new delta_name
122
+ index.parent = core_name
123
+ index.path = File.join config.searchd_file_path, index.name
124
+
125
+ sources.each_with_index do |source, i|
126
+ index.sources << source.to_riddle_for_delta(offset, i)
127
+ end
128
+
129
+ index
130
+ end
131
+
132
+ def to_riddle_for_distributed
133
+ index = Riddle::Configuration::DistributedIndex.new name
134
+ index.local_indexes << core_name
135
+ index.local_indexes.unshift delta_name if delta?
136
+ index
137
+ end
138
+
139
+ def set_configuration_options_for_indexes(index)
140
+ config.index_options.each do |key, value|
141
+ method = "#{key}=".to_sym
142
+ index.send(method, value) if index.respond_to?(method)
143
+ end
144
+
145
+ options.each do |key, value|
146
+ index.send("#{key}=".to_sym, value) if ThinkingSphinx::Configuration::IndexOptions.include?(key.to_s) && !value.nil?
147
+ end
148
+ end
149
+
150
+ def set_field_settings_for_indexes(index)
151
+ field_names = lambda { |field| field.unique_name.to_s }
152
+
153
+ index.prefix_field_names += prefix_fields.collect(&field_names)
154
+ index.infix_field_names += infix_fields.collect(&field_names)
155
+ end
98
156
  end
99
157
  end
@@ -20,9 +20,9 @@ module ThinkingSphinx
20
20
  }
21
21
  }
22
22
 
23
- def self.generate(model, &block)
23
+ def self.generate(model, name = nil, &block)
24
24
  index = ThinkingSphinx::Index.new(model)
25
- model.sphinx_facets ||= []
25
+ index.name = name unless name.nil?
26
26
 
27
27
  Builder.new(index, &block) if block_given?
28
28
 
@@ -14,7 +14,7 @@ module ThinkingSphinx
14
14
  kind_of? member? method methods nil? object_id respond_to? send should
15
15
  type )
16
16
  SafeMethods = %w( partition private_methods protected_methods
17
- public_methods send )
17
+ public_methods send class )
18
18
 
19
19
  instance_methods.select { |method|
20
20
  method.to_s[/^__/].nil? && !CoreMethods.include?(method.to_s)
@@ -58,6 +58,8 @@ module ThinkingSphinx
58
58
  end
59
59
 
60
60
  def initialize(*args)
61
+ ThinkingSphinx.context.define_indexes
62
+
61
63
  @array = []
62
64
  @options = args.extract_options!
63
65
  @args = args
@@ -90,6 +92,8 @@ module ThinkingSphinx
90
92
  if is_scope?(method)
91
93
  add_scope(method, *args, &block)
92
94
  return self
95
+ elsif method == :search_count
96
+ return scoped_count
93
97
  elsif method.to_s[/^each_with_.*/].nil? && !@array.respond_to?(method)
94
98
  super
95
99
  elsif !SafeMethods.include?(method.to_s)
@@ -146,7 +150,9 @@ module ThinkingSphinx
146
150
  # @return [Integer]
147
151
  #
148
152
  def per_page
149
- @options[:limit] || @options[:per_page] || 20
153
+ @options[:limit] ||= @options[:per_page]
154
+ @options[:limit] ||= 20
155
+ @options[:limit].to_i
150
156
  end
151
157
 
152
158
  # The total number of pages available if the results are paginated.
@@ -155,6 +161,8 @@ module ThinkingSphinx
155
161
  #
156
162
  def total_pages
157
163
  populate
164
+ return 0 if @results[:total].nil?
165
+
158
166
  @total_pages ||= (@results[:total] / per_page.to_f).ceil
159
167
  end
160
168
  # Compatibility with older versions of will_paginate
@@ -166,15 +174,18 @@ module ThinkingSphinx
166
174
  #
167
175
  def total_entries
168
176
  populate
177
+ return 0 if @results[:total_found].nil?
178
+
169
179
  @total_entries ||= @results[:total_found]
170
180
  end
171
181
 
172
182
  # The current page's offset, based on the number of records per page.
183
+ # Or explicit :offset if given.
173
184
  #
174
185
  # @return [Integer]
175
186
  #
176
187
  def offset
177
- (current_page - 1) * per_page
188
+ @options[:offset] || ((current_page - 1) * per_page)
178
189
  end
179
190
 
180
191
  def indexes
@@ -215,6 +226,7 @@ module ThinkingSphinx
215
226
  end
216
227
 
217
228
  def search(*args)
229
+ add_default_scope
218
230
  merge_search ThinkingSphinx::Search.new(*args)
219
231
  self
220
232
  end
@@ -231,8 +243,12 @@ module ThinkingSphinx
231
243
 
232
244
  retry_on_stale_index do
233
245
  begin
234
- log "Querying Sphinx: #{query}"
235
- @results = client.query query, indexes, comment
246
+ log "Querying: '#{query}'"
247
+ runtime = Benchmark.realtime {
248
+ @results = client.query query, indexes, comment
249
+ }
250
+ log "Found #{@results[:total_found]} results", :debug,
251
+ "Sphinx (#{sprintf("%f", runtime)}s)"
236
252
  rescue Errno::ECONNREFUSED => err
237
253
  raise ThinkingSphinx::ConnectionError,
238
254
  'Connection to Sphinx Daemon (searchd) failed.'
@@ -280,13 +296,16 @@ module ThinkingSphinx
280
296
  end
281
297
  end
282
298
 
283
- def self.log(message, method = :debug)
299
+ def self.log(message, method = :debug, identifier = 'Sphinx')
284
300
  return if ::ActiveRecord::Base.logger.nil?
285
- ::ActiveRecord::Base.logger.send method, message
301
+ identifier_color, message_color = "4;32;1", "0" # 0;1 = Bold
302
+ info = " \e[#{identifier_color}m#{identifier}\e[0m "
303
+ info << "\e[#{message_color}m#{message}\e[0m"
304
+ ::ActiveRecord::Base.logger.send method, info
286
305
  end
287
306
 
288
- def log(message, method = :debug)
289
- self.class.log(message, method)
307
+ def log(*args)
308
+ self.class.log(*args)
290
309
  end
291
310
 
292
311
  def client
@@ -305,6 +324,9 @@ module ThinkingSphinx
305
324
  # puts "value: #{value.inspect}"
306
325
  client.send("#{key}=", value) if value
307
326
  end
327
+
328
+ # treated non-standard as :select is already used for AR queries
329
+ client.select = options[:sphinx_select] || '*'
308
330
 
309
331
  client.limit = per_page
310
332
  client.offset = offset
@@ -684,6 +706,11 @@ MSG
684
706
  one_class && one_class.sphinx_scopes.include?(method)
685
707
  end
686
708
 
709
+ # Adds the default_sphinx_scope if set.
710
+ def add_default_scope
711
+ add_scope(one_class.get_default_sphinx_scope) if one_class && one_class.has_default_sphinx_scope?
712
+ end
713
+
687
714
  def add_scope(method, *args, &block)
688
715
  merge_search one_class.send(method, *args, &block)
689
716
  end
@@ -704,5 +731,16 @@ MSG
704
731
  end
705
732
  end
706
733
  end
734
+
735
+ def scoped_count
736
+ return self.total_entries if @options[:ids_only]
737
+
738
+ @options[:ids_only] = true
739
+ results_count = self.total_entries
740
+ @options[:ids_only] = false
741
+ @populated = false
742
+
743
+ results_count
744
+ end
707
745
  end
708
746
  end