thinking-sphinx 1.2.13 → 1.4.0

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.
Files changed (204) hide show
  1. data/README.textile +37 -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 +49 -5
  6. data/features/deleting_instances.feature +3 -0
  7. data/features/excerpts.feature +8 -0
  8. data/features/facets.feature +15 -1
  9. data/features/facets_across_model.feature +2 -2
  10. data/features/field_sorting.feature +18 -0
  11. data/features/handling_edits.feature +1 -1
  12. data/features/searching_across_models.feature +2 -2
  13. data/features/searching_by_index.feature +40 -0
  14. data/features/searching_by_model.feature +1 -8
  15. data/features/sphinx_scopes.feature +33 -0
  16. data/features/step_definitions/alpha_steps.rb +14 -1
  17. data/features/step_definitions/beta_steps.rb +1 -1
  18. data/features/step_definitions/common_steps.rb +21 -2
  19. data/features/step_definitions/facet_steps.rb +4 -0
  20. data/features/step_definitions/scope_steps.rb +8 -0
  21. data/features/step_definitions/search_steps.rb +5 -0
  22. data/features/step_definitions/sphinx_steps.rb +8 -4
  23. data/features/sti_searching.feature +5 -0
  24. data/features/support/env.rb +7 -6
  25. data/features/{support → thinking_sphinx}/db/fixtures/betas.rb +1 -0
  26. data/features/{support → thinking_sphinx}/db/fixtures/comments.rb +1 -1
  27. data/features/{support → thinking_sphinx}/db/fixtures/developers.rb +2 -0
  28. data/features/thinking_sphinx/db/fixtures/foxes.rb +3 -0
  29. data/features/thinking_sphinx/db/fixtures/music.rb +4 -0
  30. data/features/{support → thinking_sphinx}/db/fixtures/people.rb +1 -1
  31. data/features/{support → thinking_sphinx}/db/fixtures/tags.rb +1 -1
  32. data/features/{support → thinking_sphinx}/db/migrations/create_alphas.rb +1 -0
  33. data/features/{support → thinking_sphinx}/db/migrations/create_developers.rb +0 -2
  34. data/features/thinking_sphinx/db/migrations/create_genres.rb +3 -0
  35. data/features/thinking_sphinx/db/migrations/create_music.rb +6 -0
  36. data/features/thinking_sphinx/models/alpha.rb +23 -0
  37. data/features/thinking_sphinx/models/andrew.rb +17 -0
  38. data/features/{support → thinking_sphinx}/models/beta.rb +1 -1
  39. data/features/{support → thinking_sphinx}/models/developer.rb +2 -2
  40. data/features/{support → thinking_sphinx}/models/extensible_beta.rb +1 -1
  41. data/features/thinking_sphinx/models/fox.rb +5 -0
  42. data/features/thinking_sphinx/models/genre.rb +3 -0
  43. data/features/thinking_sphinx/models/medium.rb +5 -0
  44. data/features/thinking_sphinx/models/music.rb +8 -0
  45. data/features/{support → thinking_sphinx}/models/person.rb +2 -1
  46. data/features/{support → thinking_sphinx}/models/post.rb +2 -1
  47. data/lib/cucumber/thinking_sphinx/external_world.rb +12 -0
  48. data/lib/cucumber/thinking_sphinx/internal_world.rb +13 -11
  49. data/lib/thinking_sphinx/active_record/attribute_updates.rb +17 -15
  50. data/lib/thinking_sphinx/active_record/delta.rb +0 -26
  51. data/lib/thinking_sphinx/active_record/has_many_association.rb +34 -11
  52. data/lib/thinking_sphinx/active_record/scopes.rb +46 -3
  53. data/lib/thinking_sphinx/active_record.rb +271 -193
  54. data/lib/thinking_sphinx/adapters/abstract_adapter.rb +45 -9
  55. data/lib/thinking_sphinx/adapters/mysql_adapter.rb +5 -1
  56. data/lib/thinking_sphinx/adapters/postgresql_adapter.rb +9 -1
  57. data/lib/thinking_sphinx/attribute.rb +67 -23
  58. data/lib/thinking_sphinx/auto_version.rb +24 -0
  59. data/lib/thinking_sphinx/bundled_search.rb +44 -0
  60. data/lib/thinking_sphinx/class_facet.rb +3 -2
  61. data/lib/thinking_sphinx/configuration.rb +78 -64
  62. data/lib/thinking_sphinx/context.rb +76 -0
  63. data/lib/thinking_sphinx/deltas/default_delta.rb +14 -20
  64. data/lib/thinking_sphinx/deltas.rb +0 -2
  65. data/lib/thinking_sphinx/deploy/capistrano.rb +1 -1
  66. data/lib/thinking_sphinx/excerpter.rb +1 -1
  67. data/lib/thinking_sphinx/facet.rb +6 -5
  68. data/lib/thinking_sphinx/facet_search.rb +54 -24
  69. data/lib/thinking_sphinx/field.rb +2 -4
  70. data/lib/thinking_sphinx/index/builder.rb +36 -20
  71. data/lib/thinking_sphinx/index/faux_column.rb +8 -0
  72. data/lib/thinking_sphinx/index.rb +77 -19
  73. data/lib/thinking_sphinx/join.rb +37 -0
  74. data/lib/thinking_sphinx/property.rb +9 -2
  75. data/lib/thinking_sphinx/rails_additions.rb +4 -4
  76. data/lib/thinking_sphinx/search.rb +212 -66
  77. data/lib/thinking_sphinx/search_methods.rb +22 -4
  78. data/lib/thinking_sphinx/source/internal_properties.rb +2 -2
  79. data/lib/thinking_sphinx/source/sql.rb +5 -3
  80. data/lib/thinking_sphinx/source.rb +21 -12
  81. data/lib/thinking_sphinx/tasks.rb +26 -58
  82. data/lib/thinking_sphinx/test.rb +55 -0
  83. data/lib/thinking_sphinx.rb +70 -38
  84. data/rails/init.rb +4 -2
  85. data/spec/{lib/thinking_sphinx → thinking_sphinx}/active_record/delta_spec.rb +6 -8
  86. data/spec/{lib/thinking_sphinx → thinking_sphinx}/active_record/has_many_association_spec.rb +26 -3
  87. data/spec/thinking_sphinx/active_record/scopes_spec.rb +176 -0
  88. data/spec/thinking_sphinx/active_record_spec.rb +618 -0
  89. data/spec/thinking_sphinx/adapters/abstract_adapter_spec.rb +134 -0
  90. data/spec/{lib/thinking_sphinx → thinking_sphinx}/association_spec.rb +1 -1
  91. data/spec/{lib/thinking_sphinx → thinking_sphinx}/attribute_spec.rb +87 -46
  92. data/spec/thinking_sphinx/auto_version_spec.rb +47 -0
  93. data/spec/{lib/thinking_sphinx → thinking_sphinx}/configuration_spec.rb +73 -63
  94. data/spec/thinking_sphinx/context_spec.rb +127 -0
  95. data/spec/{lib/thinking_sphinx → thinking_sphinx}/core/array_spec.rb +1 -1
  96. data/spec/{lib/thinking_sphinx → thinking_sphinx}/core/string_spec.rb +1 -1
  97. data/spec/{lib/thinking_sphinx → thinking_sphinx}/excerpter_spec.rb +1 -9
  98. data/spec/{lib/thinking_sphinx → thinking_sphinx}/facet_search_spec.rb +76 -82
  99. data/spec/{lib/thinking_sphinx → thinking_sphinx}/facet_spec.rb +5 -5
  100. data/spec/{lib/thinking_sphinx → thinking_sphinx}/field_spec.rb +1 -42
  101. data/spec/{lib/thinking_sphinx → thinking_sphinx}/index/builder_spec.rb +71 -31
  102. data/spec/{lib/thinking_sphinx → thinking_sphinx}/index/faux_column_spec.rb +8 -2
  103. data/spec/thinking_sphinx/index_spec.rb +183 -0
  104. data/spec/{lib/thinking_sphinx → thinking_sphinx}/rails_additions_spec.rb +5 -5
  105. data/spec/{lib/thinking_sphinx → thinking_sphinx}/search_methods_spec.rb +5 -1
  106. data/spec/{lib/thinking_sphinx → thinking_sphinx}/search_spec.rb +183 -31
  107. data/spec/{lib/thinking_sphinx → thinking_sphinx}/source_spec.rb +18 -2
  108. data/spec/thinking_sphinx/test_spec.rb +20 -0
  109. data/spec/thinking_sphinx_spec.rb +204 -0
  110. data/tasks/distribution.rb +7 -26
  111. data/tasks/testing.rb +32 -20
  112. metadata +488 -147
  113. data/VERSION.yml +0 -5
  114. data/features/datetime_deltas.feature +0 -66
  115. data/features/delayed_delta_indexing.feature +0 -37
  116. data/features/step_definitions/datetime_delta_steps.rb +0 -15
  117. data/features/step_definitions/delayed_delta_indexing_steps.rb +0 -7
  118. data/features/support/database.yml +0 -5
  119. data/features/support/db/active_record.rb +0 -40
  120. data/features/support/db/database.yml +0 -5
  121. data/features/support/db/fixtures/delayed_betas.rb +0 -10
  122. data/features/support/db/fixtures/thetas.rb +0 -10
  123. data/features/support/db/migrations/create_delayed_betas.rb +0 -17
  124. data/features/support/db/migrations/create_thetas.rb +0 -5
  125. data/features/support/db/mysql.rb +0 -3
  126. data/features/support/db/postgresql.rb +0 -3
  127. data/features/support/models/alpha.rb +0 -10
  128. data/features/support/models/delayed_beta.rb +0 -7
  129. data/features/support/models/theta.rb +0 -7
  130. data/features/support/post_database.rb +0 -43
  131. data/lib/thinking_sphinx/deltas/datetime_delta.rb +0 -50
  132. data/lib/thinking_sphinx/deltas/delayed_delta/delta_job.rb +0 -24
  133. data/lib/thinking_sphinx/deltas/delayed_delta/flag_as_deleted_job.rb +0 -27
  134. data/lib/thinking_sphinx/deltas/delayed_delta/job.rb +0 -26
  135. data/lib/thinking_sphinx/deltas/delayed_delta.rb +0 -30
  136. data/spec/lib/thinking_sphinx/active_record/scopes_spec.rb +0 -96
  137. data/spec/lib/thinking_sphinx/active_record_spec.rb +0 -353
  138. data/spec/lib/thinking_sphinx/deltas/job_spec.rb +0 -32
  139. data/spec/lib/thinking_sphinx/index_spec.rb +0 -45
  140. data/spec/lib/thinking_sphinx_spec.rb +0 -162
  141. data/vendor/after_commit/LICENSE +0 -20
  142. data/vendor/after_commit/README +0 -16
  143. data/vendor/after_commit/Rakefile +0 -22
  144. data/vendor/after_commit/init.rb +0 -8
  145. data/vendor/after_commit/lib/after_commit/active_record.rb +0 -114
  146. data/vendor/after_commit/lib/after_commit/connection_adapters.rb +0 -103
  147. data/vendor/after_commit/lib/after_commit.rb +0 -45
  148. data/vendor/after_commit/test/after_commit_test.rb +0 -53
  149. data/vendor/delayed_job/lib/delayed/job.rb +0 -251
  150. data/vendor/delayed_job/lib/delayed/message_sending.rb +0 -7
  151. data/vendor/delayed_job/lib/delayed/performable_method.rb +0 -55
  152. data/vendor/delayed_job/lib/delayed/worker.rb +0 -54
  153. data/vendor/riddle/lib/riddle/client/filter.rb +0 -53
  154. data/vendor/riddle/lib/riddle/client/message.rb +0 -66
  155. data/vendor/riddle/lib/riddle/client/response.rb +0 -84
  156. data/vendor/riddle/lib/riddle/client.rb +0 -635
  157. data/vendor/riddle/lib/riddle/configuration/distributed_index.rb +0 -48
  158. data/vendor/riddle/lib/riddle/configuration/index.rb +0 -142
  159. data/vendor/riddle/lib/riddle/configuration/indexer.rb +0 -19
  160. data/vendor/riddle/lib/riddle/configuration/remote_index.rb +0 -17
  161. data/vendor/riddle/lib/riddle/configuration/searchd.rb +0 -25
  162. data/vendor/riddle/lib/riddle/configuration/section.rb +0 -43
  163. data/vendor/riddle/lib/riddle/configuration/source.rb +0 -23
  164. data/vendor/riddle/lib/riddle/configuration/sql_source.rb +0 -34
  165. data/vendor/riddle/lib/riddle/configuration/xml_source.rb +0 -28
  166. data/vendor/riddle/lib/riddle/configuration.rb +0 -33
  167. data/vendor/riddle/lib/riddle/controller.rb +0 -53
  168. data/vendor/riddle/lib/riddle.rb +0 -30
  169. data/features/{support → thinking_sphinx}/database.example.yml +0 -0
  170. data/features/{support → thinking_sphinx}/db/fixtures/alphas.rb +0 -0
  171. data/features/{support → thinking_sphinx}/db/fixtures/authors.rb +0 -0
  172. data/features/{support → thinking_sphinx}/db/fixtures/boxes.rb +0 -0
  173. data/features/{support → thinking_sphinx}/db/fixtures/categories.rb +0 -0
  174. data/features/{support → thinking_sphinx}/db/fixtures/cats.rb +0 -0
  175. data/features/{support → thinking_sphinx}/db/fixtures/dogs.rb +0 -0
  176. data/features/{support → thinking_sphinx}/db/fixtures/extensible_betas.rb +0 -0
  177. data/features/{support → thinking_sphinx}/db/fixtures/gammas.rb +0 -0
  178. data/features/{support → thinking_sphinx}/db/fixtures/posts.rb +0 -0
  179. data/features/{support → thinking_sphinx}/db/fixtures/robots.rb +0 -0
  180. data/features/{support → thinking_sphinx}/db/migrations/create_animals.rb +0 -0
  181. data/features/{support → thinking_sphinx}/db/migrations/create_authors.rb +0 -0
  182. data/features/{support → thinking_sphinx}/db/migrations/create_authors_posts.rb +0 -0
  183. data/features/{support → thinking_sphinx}/db/migrations/create_betas.rb +0 -0
  184. data/features/{support → thinking_sphinx}/db/migrations/create_boxes.rb +0 -0
  185. data/features/{support → thinking_sphinx}/db/migrations/create_categories.rb +0 -0
  186. data/features/{support → thinking_sphinx}/db/migrations/create_comments.rb +0 -0
  187. data/features/{support → thinking_sphinx}/db/migrations/create_extensible_betas.rb +0 -0
  188. data/features/{support → thinking_sphinx}/db/migrations/create_gammas.rb +0 -0
  189. data/features/{support → thinking_sphinx}/db/migrations/create_people.rb +0 -0
  190. data/features/{support → thinking_sphinx}/db/migrations/create_posts.rb +0 -0
  191. data/features/{support → thinking_sphinx}/db/migrations/create_robots.rb +0 -0
  192. data/features/{support → thinking_sphinx}/db/migrations/create_taggings.rb +0 -0
  193. data/features/{support → thinking_sphinx}/db/migrations/create_tags.rb +0 -0
  194. data/features/{support → thinking_sphinx}/models/animal.rb +0 -0
  195. data/features/{support → thinking_sphinx}/models/author.rb +0 -0
  196. data/features/{support → thinking_sphinx}/models/box.rb +0 -0
  197. data/features/{support → thinking_sphinx}/models/cat.rb +0 -0
  198. data/features/{support → thinking_sphinx}/models/category.rb +0 -0
  199. data/features/{support → thinking_sphinx}/models/comment.rb +3 -3
  200. /data/features/{support → thinking_sphinx}/models/dog.rb +0 -0
  201. /data/features/{support → thinking_sphinx}/models/gamma.rb +0 -0
  202. /data/features/{support → thinking_sphinx}/models/robot.rb +0 -0
  203. /data/features/{support → thinking_sphinx}/models/tag.rb +0 -0
  204. /data/features/{support → thinking_sphinx}/models/tagging.rb +0 -0
@@ -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
 
@@ -42,29 +44,27 @@ module ThinkingSphinx
42
44
  end
43
45
 
44
46
  def populate
45
- facet_names.each do |name|
46
- search_options = facet_search_options.merge(:group_by => name)
47
- add_from_results name, ThinkingSphinx.search(
48
- *(args + [search_options])
49
- )
50
- end
47
+ ThinkingSphinx::Search.bundle_searches(facet_names) { |sphinx, name|
48
+ sphinx.search *(args + [facet_search_options(name)])
49
+ }.each_with_index { |search, index|
50
+ add_from_results facet_names[index], search
51
+ }
51
52
  end
52
53
 
53
- def facet_search_options
54
- config = ThinkingSphinx::Configuration.instance
55
- max = config.configuration.searchd.max_matches || 1000
56
-
54
+ def facet_search_options(facet_name)
57
55
  options.merge(
58
56
  :group_function => :attr,
59
- :limit => max,
60
- :max_matches => max,
61
- :page => 1
57
+ :limit => max_matches,
58
+ :max_matches => max_matches,
59
+ :page => 1,
60
+ :group_by => facet_name,
61
+ :ids_only => !translate?(facet_name)
62
62
  )
63
63
  end
64
64
 
65
65
  def facet_classes
66
66
  (
67
- options[:classes] || ThinkingSphinx.indexed_models.collect { |model|
67
+ options[:classes] || ThinkingSphinx.context.indexed_models.collect { |model|
68
68
  model.constantize
69
69
  }
70
70
  ).select { |klass| klass.sphinx_facets.any? }
@@ -99,21 +99,34 @@ module ThinkingSphinx
99
99
  }
100
100
  end
101
101
 
102
- def add_from_results(facet, results)
103
- name = ThinkingSphinx::Facet.name_for(facet)
102
+ def translate?(name)
103
+ facet = facet_from_name(name)
104
+ facet.translate? || facet.float?
105
+ end
106
+
107
+ def config
108
+ ThinkingSphinx::Configuration.instance
109
+ end
110
+
111
+ def max_matches
112
+ @max_matches ||= config.configuration.searchd.max_matches || 1000
113
+ end
114
+
115
+ # example: facet = country_facet; name = :country
116
+ def add_from_results(facet, search)
117
+ name = ThinkingSphinx::Facet.name_for(facet)
118
+ facet = facet_from_name(facet)
104
119
 
105
120
  self[name] ||= {}
106
121
 
107
- return if results.empty?
122
+ return if search.empty?
108
123
 
109
- facet = facet_from_object(results.first, facet) if facet.is_a?(String)
110
-
111
- results.each_with_groupby_and_count { |result, group, count|
112
- facet_value = facet.value(result, group)
124
+ search.each_with_match do |result, match|
125
+ facet_value = facet.value(result, match[:attributes])
113
126
 
114
127
  self[name][facet_value] ||= 0
115
- self[name][facet_value] += count
116
- }
128
+ self[name][facet_value] += match[:attributes]["@count"]
129
+ end
117
130
  end
118
131
 
119
132
  def underlying_value(key, value)
@@ -128,7 +141,24 @@ module ThinkingSphinx
128
141
  end
129
142
 
130
143
  def facet_from_object(object, name)
131
- object.sphinx_facets.detect { |facet| facet.attribute_name == name }
144
+ facet = nil
145
+ klass = object.class
146
+
147
+ while klass != ::ActiveRecord::Base && facet.nil?
148
+ facet = klass.sphinx_facets.detect { |facet|
149
+ facet.attribute_name == name
150
+ }
151
+ klass = klass.superclass
152
+ end
153
+
154
+ facet
155
+ end
156
+
157
+ def facet_from_name(name)
158
+ name = ThinkingSphinx::Facet.name_for(name)
159
+ all_facets.detect { |facet|
160
+ facet.name == name
161
+ }
132
162
  end
133
163
  end
134
164
  end
@@ -69,14 +69,12 @@ module ThinkingSphinx
69
69
  # multiple data values (has_many or has_and_belongs_to_many associations).
70
70
  #
71
71
  def to_select_sql
72
- clause = @columns.collect { |column|
73
- column_with_prefix(column)
74
- }.join(', ')
72
+ clause = columns_with_prefixes.join(', ')
75
73
 
76
74
  clause = adapter.concatenate(clause) if concat_ws?
77
75
  clause = adapter.group_concatenate(clause) if is_many?
78
76
 
79
- "#{adapter.cast_to_string clause } AS #{quote_column(unique_name)}"
77
+ "#{clause} AS #{quote_column(unique_name)}"
80
78
  end
81
79
  end
82
80
  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
 
@@ -32,15 +32,11 @@ module ThinkingSphinx
32
32
 
33
33
  def initialize(index, &block)
34
34
  @index = index
35
- @source = ThinkingSphinx::Source.new(@index)
36
- @index.sources << @source
37
35
  @explicit_source = false
38
36
 
39
37
  self.instance_eval &block
40
38
 
41
- if @index.sources.any? { |source|
42
- source.fields.length == 0
43
- }
39
+ if no_fields?
44
40
  raise "At least one field is necessary for an index"
45
41
  end
46
42
  end
@@ -105,7 +101,7 @@ module ThinkingSphinx
105
101
  def indexes(*args)
106
102
  options = args.extract_options!
107
103
  args.each do |columns|
108
- field = Field.new(@source, FauxColumn.coerce(columns), options)
104
+ field = Field.new(source, FauxColumn.coerce(columns), options)
109
105
 
110
106
  add_sort_attribute field, options if field.sortable
111
107
  add_facet_attribute field, options if field.faceted
@@ -123,13 +119,13 @@ module ThinkingSphinx
123
119
  # database.
124
120
  #
125
121
  # Attributes are limited to the following types: integers, floats,
126
- # datetimes (converted to timestamps), booleans and strings. Don't
127
- # forget that Sphinx converts string attributes to integers, which are
128
- # useful for sorting, but that's about it.
122
+ # datetimes (converted to timestamps), booleans, strings and MVAs
123
+ # (:multi). Don't forget that Sphinx converts string attributes to
124
+ # integers, which are useful for sorting, but that's about it.
129
125
  #
130
- # You can also have a collection of integers for multi-value attributes
131
- # (MVAs). Generally these would be through a has_many relationship,
132
- # like in this example:
126
+ # Collection of integers are known as multi-value attributes (MVAs).
127
+ # Generally these would be through a has_many relationship, like in this
128
+ # example:
133
129
  #
134
130
  # has posts(:id), :as => :post_ids
135
131
  #
@@ -151,7 +147,7 @@ module ThinkingSphinx
151
147
  def has(*args)
152
148
  options = args.extract_options!
153
149
  args.each do |columns|
154
- attribute = Attribute.new(@source, FauxColumn.coerce(columns), options)
150
+ attribute = Attribute.new(source, FauxColumn.coerce(columns), options)
155
151
 
156
152
  add_facet_attribute attribute, options if attribute.faceted
157
153
  end
@@ -162,12 +158,18 @@ module ThinkingSphinx
162
158
  options[:facet] = true
163
159
 
164
160
  args.each do |columns|
165
- attribute = Attribute.new(@source, FauxColumn.coerce(columns), options)
161
+ attribute = Attribute.new(source, FauxColumn.coerce(columns), options)
166
162
 
167
163
  add_facet_attribute attribute, options
168
164
  end
169
165
  end
170
166
 
167
+ def join(*args)
168
+ args.each do |association|
169
+ Join.new(source, association)
170
+ end
171
+ end
172
+
171
173
  # Use this method to add some manual SQL conditions for your index
172
174
  # request. You can pass in as many strings as you like, they'll get
173
175
  # joined together with ANDs later on.
@@ -176,7 +178,7 @@ module ThinkingSphinx
176
178
  # where "parent_type = 'Article'", "created_at < NOW()"
177
179
  #
178
180
  def where(*args)
179
- @source.conditions += args
181
+ source.conditions += args
180
182
  end
181
183
 
182
184
  # Use this method to add some manual SQL strings to the GROUP BY
@@ -186,7 +188,7 @@ module ThinkingSphinx
186
188
  # group_by "lat", "lng"
187
189
  #
188
190
  def group_by(*args)
189
- @source.groupings += args
191
+ source.groupings += args
190
192
  end
191
193
 
192
194
  # This is what to use to set properties on the index. Chief amongst
@@ -251,10 +253,18 @@ module ThinkingSphinx
251
253
 
252
254
  private
253
255
 
256
+ def source
257
+ @source ||= begin
258
+ source = ThinkingSphinx::Source.new(@index)
259
+ @index.sources << source
260
+ source
261
+ end
262
+ end
263
+
254
264
  def set_single_property(key, value)
255
265
  source_options = ThinkingSphinx::Configuration::SourceOptions
256
266
  if source_options.include?(key.to_s)
257
- @source.options.merge! key => value
267
+ source.options.merge! key => value
258
268
  else
259
269
  @index.local_options.merge! key => value
260
270
  end
@@ -272,7 +282,7 @@ module ThinkingSphinx
272
282
  def add_internal_attribute(property, options, suffix, crc = false)
273
283
  return unless ThinkingSphinx::Facet.translate?(property)
274
284
 
275
- Attribute.new(@source,
285
+ Attribute.new(source,
276
286
  property.columns.collect { |col| col.clone },
277
287
  options.merge(
278
288
  :type => property.is_a?(Field) ? :string : options[:type],
@@ -281,6 +291,12 @@ module ThinkingSphinx
281
291
  ).except(:facet)
282
292
  )
283
293
  end
294
+
295
+ def no_fields?
296
+ @index.sources.empty? || @index.sources.any? { |source|
297
+ source.fields.length == 0
298
+ }
299
+ end
284
300
  end
285
301
  end
286
302
  end
@@ -42,6 +42,10 @@ module ThinkingSphinx
42
42
  @stack
43
43
  end
44
44
 
45
+ def __path
46
+ @stack + [@name]
47
+ end
48
+
45
49
  # Returns true if the stack is empty *and* if the name is a string -
46
50
  # which is an indication that of raw SQL, as opposed to a value from a
47
51
  # table's column.
@@ -50,6 +54,10 @@ module ThinkingSphinx
50
54
  @name.is_a?(String) && @stack.empty?
51
55
  end
52
56
 
57
+ def to_ary
58
+ [self]
59
+ end
60
+
53
61
  # This handles any 'invalid' method calls and sets them as the name,
54
62
  # and pushing the previous name into the stack. The object returns
55
63
  # itself.
@@ -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
@@ -0,0 +1,37 @@
1
+ module ThinkingSphinx
2
+ class Join
3
+ attr_accessor :source, :column, :associations
4
+
5
+ def initialize(source, column)
6
+ @source = source
7
+ @column = column
8
+
9
+ @associations = association_stack(column.__path.clone).each { |assoc|
10
+ assoc.join_to(source.base)
11
+ }
12
+
13
+ source.joins << self
14
+ end
15
+
16
+ private
17
+
18
+ # Gets a stack of associations for a specific path.
19
+ #
20
+ def association_stack(path, parent = nil)
21
+ assocs = []
22
+
23
+ if parent.nil?
24
+ assocs = @source.association(path.shift)
25
+ else
26
+ assocs = parent.children(path.shift)
27
+ end
28
+
29
+ until path.empty?
30
+ point = path.shift
31
+ assocs = assocs.collect { |assoc| assoc.children(point) }.flatten
32
+ end
33
+
34
+ assocs
35
+ end
36
+ end
37
+ end
@@ -13,6 +13,7 @@ module ThinkingSphinx
13
13
  @alias = options[:as]
14
14
  @faceted = options[:facet]
15
15
  @admin = options[:admin]
16
+ @sortable = options[:sortable] || false
16
17
 
17
18
  @alias = @alias.to_sym unless @alias.blank?
18
19
 
@@ -136,10 +137,16 @@ module ThinkingSphinx
136
137
  assoc.has_column?(column.__name) ?
137
138
  "#{quote_with_table(assoc.join.aliased_table_name, column.__name)}" :
138
139
  nil
139
- }.compact.join(', ')
140
+ }.compact
140
141
  end
141
142
  end
142
143
 
144
+ def columns_with_prefixes
145
+ @columns.collect { |column|
146
+ column_with_prefix column
147
+ }.flatten
148
+ end
149
+
143
150
  # Gets a stack of associations for a specific path.
144
151
  #
145
152
  def association_stack(path, parent = nil)
@@ -159,4 +166,4 @@ module ThinkingSphinx
159
166
  assocs
160
167
  end
161
168
  end
162
- end
169
+ end
@@ -136,8 +136,8 @@ Class.extend(
136
136
  ) unless Class.respond_to?(:cattr_reader)
137
137
 
138
138
  module ThinkingSphinx
139
- module MetaClass
140
- def metaclass
139
+ module SingletonClass
140
+ def singleton_class
141
141
  class << self
142
142
  self
143
143
  end
@@ -145,6 +145,6 @@ module ThinkingSphinx
145
145
  end
146
146
  end
147
147
 
148
- unless Object.new.respond_to?(:metaclass)
149
- Object.send(:include, ThinkingSphinx::MetaClass)
148
+ unless Object.new.respond_to?(:singleton_class)
149
+ Object.send(:include, ThinkingSphinx::SingletonClass)
150
150
  end