thinking-sphinx 1.5.0 → 2.0.0.rc1

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 (104) hide show
  1. data/README.textile +15 -48
  2. data/VERSION +1 -0
  3. data/features/attribute_transformation.feature +7 -7
  4. data/features/attribute_updates.feature +16 -18
  5. data/features/deleting_instances.feature +13 -16
  6. data/features/excerpts.feature +0 -8
  7. data/features/facets.feature +19 -25
  8. data/features/handling_edits.feature +20 -25
  9. data/features/searching_across_models.feature +1 -1
  10. data/features/searching_by_index.feature +5 -6
  11. data/features/searching_by_model.feature +29 -29
  12. data/features/sphinx_scopes.feature +0 -26
  13. data/features/step_definitions/common_steps.rb +6 -18
  14. data/features/step_definitions/scope_steps.rb +0 -4
  15. data/features/step_definitions/search_steps.rb +4 -9
  16. data/features/support/env.rb +10 -3
  17. data/features/thinking_sphinx/db/fixtures/alphas.rb +10 -8
  18. data/features/thinking_sphinx/db/fixtures/cats.rb +1 -1
  19. data/features/thinking_sphinx/db/fixtures/dogs.rb +1 -1
  20. data/features/thinking_sphinx/db/fixtures/foxes.rb +1 -1
  21. data/features/thinking_sphinx/db/fixtures/people.rb +1 -1
  22. data/features/thinking_sphinx/db/fixtures/posts.rb +1 -5
  23. data/features/thinking_sphinx/db/migrations/create_posts.rb +0 -1
  24. data/features/thinking_sphinx/models/alpha.rb +0 -1
  25. data/features/thinking_sphinx/models/beta.rb +0 -5
  26. data/features/thinking_sphinx/models/developer.rb +1 -6
  27. data/features/thinking_sphinx/models/music.rb +1 -3
  28. data/features/thinking_sphinx/models/person.rb +1 -2
  29. data/features/thinking_sphinx/models/post.rb +0 -1
  30. data/lib/cucumber/thinking_sphinx/external_world.rb +4 -8
  31. data/lib/cucumber/thinking_sphinx/internal_world.rb +27 -36
  32. data/lib/thinking_sphinx.rb +60 -132
  33. data/lib/thinking_sphinx/active_record.rb +98 -124
  34. data/lib/thinking_sphinx/active_record/attribute_updates.rb +13 -17
  35. data/lib/thinking_sphinx/active_record/delta.rb +15 -21
  36. data/lib/thinking_sphinx/active_record/has_many_association.rb +23 -16
  37. data/lib/thinking_sphinx/active_record/scopes.rb +0 -18
  38. data/lib/thinking_sphinx/adapters/abstract_adapter.rb +15 -63
  39. data/lib/thinking_sphinx/adapters/mysql_adapter.rb +0 -4
  40. data/lib/thinking_sphinx/adapters/postgresql_adapter.rb +24 -65
  41. data/lib/thinking_sphinx/association.rb +11 -36
  42. data/lib/thinking_sphinx/attribute.rb +85 -92
  43. data/lib/thinking_sphinx/auto_version.rb +3 -21
  44. data/lib/thinking_sphinx/class_facet.rb +3 -8
  45. data/lib/thinking_sphinx/configuration.rb +58 -114
  46. data/lib/thinking_sphinx/context.rb +20 -22
  47. data/lib/thinking_sphinx/core/array.rb +13 -0
  48. data/lib/thinking_sphinx/deltas.rb +0 -2
  49. data/lib/thinking_sphinx/deltas/default_delta.rb +22 -18
  50. data/lib/thinking_sphinx/deploy/capistrano.rb +31 -30
  51. data/lib/thinking_sphinx/excerpter.rb +1 -2
  52. data/lib/thinking_sphinx/facet.rb +35 -45
  53. data/lib/thinking_sphinx/facet_search.rb +24 -58
  54. data/lib/thinking_sphinx/field.rb +0 -18
  55. data/lib/thinking_sphinx/index.rb +36 -38
  56. data/lib/thinking_sphinx/index/builder.rb +59 -74
  57. data/lib/thinking_sphinx/property.rb +45 -66
  58. data/lib/thinking_sphinx/railtie.rb +35 -0
  59. data/lib/thinking_sphinx/search.rb +250 -506
  60. data/lib/thinking_sphinx/source.rb +31 -50
  61. data/lib/thinking_sphinx/source/internal_properties.rb +3 -8
  62. data/lib/thinking_sphinx/source/sql.rb +31 -71
  63. data/lib/thinking_sphinx/tasks.rb +27 -48
  64. data/spec/thinking_sphinx/active_record/delta_spec.rb +41 -36
  65. data/spec/thinking_sphinx/active_record/has_many_association_spec.rb +0 -96
  66. data/spec/thinking_sphinx/active_record/scopes_spec.rb +29 -29
  67. data/spec/thinking_sphinx/active_record_spec.rb +169 -140
  68. data/spec/thinking_sphinx/association_spec.rb +2 -20
  69. data/spec/thinking_sphinx/attribute_spec.rb +97 -101
  70. data/spec/thinking_sphinx/auto_version_spec.rb +11 -75
  71. data/spec/thinking_sphinx/configuration_spec.rb +62 -63
  72. data/spec/thinking_sphinx/context_spec.rb +66 -66
  73. data/spec/thinking_sphinx/facet_search_spec.rb +99 -99
  74. data/spec/thinking_sphinx/facet_spec.rb +4 -30
  75. data/spec/thinking_sphinx/field_spec.rb +3 -17
  76. data/spec/thinking_sphinx/index/builder_spec.rb +132 -169
  77. data/spec/thinking_sphinx/index_spec.rb +39 -45
  78. data/spec/thinking_sphinx/search_methods_spec.rb +33 -37
  79. data/spec/thinking_sphinx/search_spec.rb +269 -491
  80. data/spec/thinking_sphinx/source_spec.rb +48 -62
  81. data/spec/thinking_sphinx_spec.rb +49 -49
  82. data/tasks/distribution.rb +46 -0
  83. data/tasks/testing.rb +74 -0
  84. metadata +123 -199
  85. data/features/field_sorting.feature +0 -18
  86. data/features/thinking_sphinx/db/.gitignore +0 -1
  87. data/features/thinking_sphinx/db/fixtures/post_keywords.txt +0 -1
  88. data/features/thinking_sphinx/models/andrew.rb +0 -17
  89. data/lib/thinking-sphinx.rb +0 -1
  90. data/lib/thinking_sphinx/active_record/has_many_association_with_scopes.rb +0 -21
  91. data/lib/thinking_sphinx/bundled_search.rb +0 -40
  92. data/lib/thinking_sphinx/connection.rb +0 -71
  93. data/lib/thinking_sphinx/deltas/delete_job.rb +0 -16
  94. data/lib/thinking_sphinx/deltas/index_job.rb +0 -17
  95. data/lib/thinking_sphinx/rails_additions.rb +0 -181
  96. data/spec/fixtures/data.sql +0 -32
  97. data/spec/fixtures/database.yml.default +0 -3
  98. data/spec/fixtures/models.rb +0 -161
  99. data/spec/fixtures/structure.sql +0 -146
  100. data/spec/spec_helper.rb +0 -54
  101. data/spec/sphinx_helper.rb +0 -67
  102. data/spec/thinking_sphinx/adapters/abstract_adapter_spec.rb +0 -163
  103. data/spec/thinking_sphinx/connection_spec.rb +0 -77
  104. data/spec/thinking_sphinx/rails_additions_spec.rb +0 -203
@@ -19,8 +19,6 @@ module ThinkingSphinx
19
19
  # - :sortable => true
20
20
  # - :infixes => true
21
21
  # - :prefixes => true
22
- # - :file => true
23
- # - :with => :attribute # or :wordcount
24
22
  #
25
23
  # Alias is only required in three circumstances: when there's
26
24
  # another attribute or field with the same name, when the column name is
@@ -60,8 +58,6 @@ module ThinkingSphinx
60
58
  @sortable = options[:sortable] || false
61
59
  @infixes = options[:infixes] || false
62
60
  @prefixes = options[:prefixes] || false
63
- @file = options[:file] || false
64
- @with = options[:with]
65
61
 
66
62
  source.fields << self
67
63
  end
@@ -73,8 +69,6 @@ module ThinkingSphinx
73
69
  # multiple data values (has_many or has_and_belongs_to_many associations).
74
70
  #
75
71
  def to_select_sql
76
- return nil unless available?
77
-
78
72
  clause = columns_with_prefixes.join(', ')
79
73
 
80
74
  clause = adapter.concatenate(clause) if concat_ws?
@@ -82,17 +76,5 @@ module ThinkingSphinx
82
76
 
83
77
  "#{clause} AS #{quote_column(unique_name)}"
84
78
  end
85
-
86
- def file?
87
- @file
88
- end
89
-
90
- def with_attribute?
91
- @with == :attribute
92
- end
93
-
94
- def with_wordcount?
95
- @with == :wordcount
96
- end
97
79
  end
98
80
  end
@@ -3,8 +3,8 @@ require 'thinking_sphinx/index/faux_column'
3
3
 
4
4
  module ThinkingSphinx
5
5
  class Index
6
- attr_accessor :name, :model, :sources, :delta_object, :additional_indices
7
-
6
+ attr_accessor :name, :model, :sources, :delta_object
7
+
8
8
  # Create a new index instance by passing in the model it is tied to, and
9
9
  # a block to build it with (optional but recommended). For documentation
10
10
  # on the syntax for inside the block, the Builder class is what you want.
@@ -13,9 +13,9 @@ module ThinkingSphinx
13
13
  #
14
14
  # Index.new(User) do
15
15
  # indexes login, email
16
- #
16
+ #
17
17
  # has created_at
18
- #
18
+ #
19
19
  # set_property :delta => true
20
20
  # end
21
21
  #
@@ -25,48 +25,47 @@ module ThinkingSphinx
25
25
  @sources = []
26
26
  @options = {}
27
27
  @delta_object = nil
28
- @additional_indices = []
29
28
  end
30
-
29
+
31
30
  def fields
32
31
  @sources.collect { |source| source.fields }.flatten
33
32
  end
34
-
33
+
35
34
  def attributes
36
35
  @sources.collect { |source| source.attributes }.flatten
37
36
  end
38
-
37
+
39
38
  def core_name
40
39
  "#{name}_core"
41
40
  end
42
-
41
+
43
42
  def delta_name
44
43
  "#{name}_delta"
45
44
  end
46
-
45
+
47
46
  def all_names
48
47
  names = [core_name]
49
48
  names << delta_name if delta?
50
-
49
+
51
50
  names
52
51
  end
53
-
52
+
54
53
  def self.name_for(model)
55
54
  model.name.underscore.tr(':/\\', '_')
56
55
  end
57
-
56
+
58
57
  def prefix_fields
59
58
  fields.select { |field| field.prefixes }
60
59
  end
61
-
60
+
62
61
  def infix_fields
63
62
  fields.select { |field| field.infixes }
64
63
  end
65
-
64
+
66
65
  def local_options
67
66
  @options
68
67
  end
69
-
68
+
70
69
  def options
71
70
  all_index_options = config.index_options.clone
72
71
  @options.keys.select { |key|
@@ -75,83 +74,82 @@ module ThinkingSphinx
75
74
  }.each { |key| all_index_options[key.to_sym] = @options[key] }
76
75
  all_index_options
77
76
  end
78
-
77
+
79
78
  def delta?
80
79
  !@delta_object.nil?
81
80
  end
82
-
81
+
83
82
  def to_riddle(offset)
84
83
  indexes = [to_riddle_for_core(offset)]
85
84
  indexes << to_riddle_for_delta(offset) if delta?
86
85
  indexes << to_riddle_for_distributed
87
86
  end
88
-
87
+
89
88
  private
90
-
89
+
91
90
  def adapter
92
91
  @adapter ||= @model.sphinx_database_adapter
93
92
  end
94
-
93
+
95
94
  def utf8?
96
95
  options[:charset_type] == "utf-8"
97
96
  end
98
-
97
+
99
98
  def sql_query_pre_for_delta
100
99
  [""]
101
100
  end
102
-
101
+
103
102
  def config
104
103
  @config ||= ThinkingSphinx::Configuration.instance
105
104
  end
106
-
105
+
107
106
  def to_riddle_for_core(offset)
108
107
  index = Riddle::Configuration::Index.new core_name
109
108
  index.path = File.join config.searchd_file_path, index.name
110
-
109
+
111
110
  set_configuration_options_for_indexes index
112
111
  set_field_settings_for_indexes index
113
-
112
+
114
113
  sources.each_with_index do |source, i|
115
114
  index.sources << source.to_riddle_for_core(offset, i)
116
115
  end
117
-
116
+
118
117
  index
119
118
  end
120
-
119
+
121
120
  def to_riddle_for_delta(offset)
122
121
  index = Riddle::Configuration::Index.new delta_name
123
122
  index.parent = core_name
124
123
  index.path = File.join config.searchd_file_path, index.name
125
-
124
+
126
125
  sources.each_with_index do |source, i|
127
126
  index.sources << source.to_riddle_for_delta(offset, i)
128
127
  end
129
-
128
+
130
129
  index
131
130
  end
132
-
131
+
133
132
  def to_riddle_for_distributed
134
133
  index = Riddle::Configuration::DistributedIndex.new name
135
- index.local_indices << core_name
136
- index.local_indices += additional_indices
137
- index.local_indices.unshift delta_name if delta?
134
+ index.local_indexes << core_name
135
+ index.local_indexes.unshift delta_name if delta?
138
136
  index
139
137
  end
140
-
138
+
141
139
  def set_configuration_options_for_indexes(index)
142
140
  config.index_options.each do |key, value|
143
141
  method = "#{key}=".to_sym
144
142
  index.send(method, value) if index.respond_to?(method)
145
143
  end
146
-
144
+
147
145
  options.each do |key, value|
148
146
  index.send("#{key}=".to_sym, value) if ThinkingSphinx::Configuration::IndexOptions.include?(key.to_s) && !value.nil?
149
147
  end
150
148
  end
151
-
149
+
152
150
  def set_field_settings_for_indexes(index)
153
151
  field_names = lambda { |field| field.unique_name.to_s }
154
-
152
+
155
153
  index.prefix_field_names += prefix_fields.collect(&field_names)
156
154
  index.infix_field_names += infix_fields.collect(&field_names)
157
155
  end
@@ -11,7 +11,7 @@ module ThinkingSphinx
11
11
  # your indexes. #where provides a method to add manual SQL conditions, and
12
12
  # set_property allows you to set some settings on a per-index basis. Check
13
13
  # out each method's documentation for better ideas of usage.
14
- #
14
+ #
15
15
  class Builder
16
16
  instance_methods.grep(/^[^_]/).each { |method|
17
17
  next if method.to_s == "instance_eval"
@@ -19,28 +19,28 @@ module ThinkingSphinx
19
19
  caller.grep(/irb.completion/).empty? ? method_missing(method) : super
20
20
  }
21
21
  }
22
-
22
+
23
23
  def self.generate(model, name = nil, &block)
24
24
  index = ThinkingSphinx::Index.new(model)
25
25
  index.name = name unless name.nil?
26
-
26
+
27
27
  Builder.new(index, &block) if block_given?
28
-
28
+
29
29
  index.delta_object = ThinkingSphinx::Deltas.parse index
30
30
  index
31
31
  end
32
-
32
+
33
33
  def initialize(index, &block)
34
34
  @index = index
35
35
  @explicit_source = false
36
-
36
+
37
37
  self.instance_eval &block
38
-
38
+
39
39
  if no_fields?
40
40
  raise "At least one field is necessary for an index"
41
41
  end
42
42
  end
43
-
43
+
44
44
  def define_source(&block)
45
45
  if @explicit_source
46
46
  @source = ThinkingSphinx::Source.new(@index)
@@ -48,15 +48,10 @@ module ThinkingSphinx
48
48
  else
49
49
  @explicit_source = true
50
50
  end
51
-
51
+
52
52
  self.instance_eval &block
53
53
  end
54
-
55
- def use_local_indices(*indexes)
56
- @index.additional_indices += indexes.map {|index_name| "#{index_name.to_s}_core"}
57
- end
58
- alias_method :use_local_index, :use_local_indices
59
-
54
+
60
55
  # This is how you add fields - the strings Sphinx looks at - to your
61
56
  # index. Technically, to use this method, you need to pass in some
62
57
  # columns and options - but there's some neat method_missing stuff
@@ -68,26 +63,26 @@ module ThinkingSphinx
68
63
  # field.
69
64
  #
70
65
  # Adding Single-Column Fields:
71
- #
66
+ #
72
67
  # You can use symbols or methods - and can chain methods together to
73
68
  # get access down the associations tree.
74
- #
69
+ #
75
70
  # indexes :id, :as => :my_id
76
71
  # indexes :name, :sortable => true
77
72
  # indexes first_name, last_name, :sortable => true
78
73
  # indexes users.posts.content, :as => :post_content
79
74
  # indexes users(:id), :as => :user_ids
80
75
  #
81
- # Keep in mind that if any keywords for Ruby methods - such as id or
76
+ # Keep in mind that if any keywords for Ruby methods - such as id or
82
77
  # name - clash with your column names, you need to use the symbol
83
78
  # version (see the first, second and last examples above).
84
79
  #
85
80
  # If you specify multiple columns (example #2), a field will be created
86
81
  # for each. Don't use the :as option in this case. If you want to merge
87
82
  # those columns together, continue reading.
88
- #
83
+ #
89
84
  # Adding Multi-Column Fields:
90
- #
85
+ #
91
86
  # indexes [first_name, last_name], :as => :name
92
87
  # indexes [location, parent.location], :as => :location
93
88
  #
@@ -95,7 +90,7 @@ module ThinkingSphinx
95
90
  # them in an Array, as shown by the above examples. There's no
96
91
  # limitations on whether they're symbols or methods or what level of
97
92
  # associations they come from.
98
- #
93
+ #
99
94
  # Adding SQL Fragment Fields
100
95
  #
101
96
  # You can also define a field using an SQL fragment, useful for when
@@ -107,37 +102,37 @@ module ThinkingSphinx
107
102
  options = args.extract_options!
108
103
  args.each do |columns|
109
104
  field = Field.new(source, FauxColumn.coerce(columns), options)
110
-
105
+
111
106
  add_sort_attribute field, options if field.sortable
112
107
  add_facet_attribute field, options if field.faceted
113
108
  end
114
109
  end
115
-
110
+
116
111
  # This is the method to add attributes to your index (hence why it is
117
112
  # aliased as 'attribute'). The syntax is the same as #indexes, so use
118
113
  # that as starting point, but keep in mind the following points.
119
- #
114
+ #
120
115
  # An attribute can have an alias (the :as option), but it is always
121
116
  # sortable - so you don't need to explicitly request that. You _can_
122
117
  # specify the data type of the attribute (the :type option), but the
123
118
  # code's pretty good at figuring that out itself from peering into the
124
119
  # database.
125
- #
120
+ #
126
121
  # Attributes are limited to the following types: integers, floats,
127
122
  # datetimes (converted to timestamps), booleans, strings and MVAs
128
123
  # (:multi). Don't forget that Sphinx converts string attributes to
129
124
  # integers, which are useful for sorting, but that's about it.
130
- #
125
+ #
131
126
  # Collection of integers are known as multi-value attributes (MVAs).
132
127
  # Generally these would be through a has_many relationship, like in this
133
128
  # example:
134
- #
129
+ #
135
130
  # has posts(:id), :as => :post_ids
136
- #
131
+ #
137
132
  # This allows you to filter on any of the values tied to a specific
138
133
  # record. Might be best to read through the Sphinx documentation to get
139
134
  # a better idea of that though.
140
- #
135
+ #
141
136
  # Adding SQL Fragment Attributes
142
137
  #
143
138
  # You can also define an attribute using an SQL fragment, useful for
@@ -145,68 +140,68 @@ module ThinkingSphinx
145
140
  # the type of the attribute though:
146
141
  #
147
142
  # has "age < 18", :as => :minor, :type => :boolean
148
- #
143
+ #
149
144
  # If you're creating attributes for latitude and longitude, don't
150
145
  # forget that Sphinx expects these values to be in radians.
151
- #
146
+ #
152
147
  def has(*args)
153
148
  options = args.extract_options!
154
149
  args.each do |columns|
155
150
  attribute = Attribute.new(source, FauxColumn.coerce(columns), options)
156
-
151
+
157
152
  add_facet_attribute attribute, options if attribute.faceted
158
153
  end
159
154
  end
160
-
155
+
161
156
  def facet(*args)
162
157
  options = args.extract_options!
163
158
  options[:facet] = true
164
-
159
+
165
160
  args.each do |columns|
166
161
  attribute = Attribute.new(source, FauxColumn.coerce(columns), options)
167
-
162
+
168
163
  add_facet_attribute attribute, options
169
164
  end
170
165
  end
171
-
166
+
172
167
  def join(*args)
173
168
  args.each do |association|
174
169
  Join.new(source, association)
175
170
  end
176
171
  end
177
-
172
+
178
173
  # Use this method to add some manual SQL conditions for your index
179
174
  # request. You can pass in as many strings as you like, they'll get
180
175
  # joined together with ANDs later on.
181
- #
176
+ #
182
177
  # where "user_id = 10"
183
178
  # where "parent_type = 'Article'", "created_at < NOW()"
184
- #
179
+ #
185
180
  def where(*args)
186
181
  source.conditions += args
187
182
  end
188
-
183
+
189
184
  # Use this method to add some manual SQL strings to the GROUP BY
190
185
  # clause. You can pass in as many strings as you'd like, they'll get
191
186
  # joined together with commas later on.
192
- #
187
+ #
193
188
  # group_by "lat", "lng"
194
- #
189
+ #
195
190
  def group_by(*args)
196
191
  source.groupings += args
197
192
  end
198
-
193
+
199
194
  # This is what to use to set properties on the index. Chief amongst
200
195
  # those is the delta property - to allow automatic updates to your
201
196
  # indexes as new models are added and edited - but also you can
202
197
  # define search-related properties which will be the defaults for all
203
198
  # searches on the model.
204
- #
199
+ #
205
200
  # set_property :delta => true
206
201
  # set_property :field_weights => {"name" => 100}
207
202
  # set_property :order => "name ASC"
208
203
  # set_property :select => 'name'
209
- #
204
+ #
210
205
  # Also, the following two properties are particularly relevant for
211
206
  # geo-location searching - latitude_attr and longitude_attr. If your
212
207
  # attributes for these two values are named something other than
@@ -215,59 +210,49 @@ module ThinkingSphinx
215
210
  # geo-related search.
216
211
  #
217
212
  # set_property :latitude_attr => "lt", :longitude_attr => "lg"
218
- #
213
+ #
219
214
  # Please don't forget to add a boolean field named 'delta' to your
220
215
  # model's database table if enabling the delta index for it.
221
216
  # Valid options for the delta property are:
222
- #
217
+ #
223
218
  # true
224
219
  # false
225
220
  # :default
226
221
  # :delayed
227
222
  # :datetime
228
- #
229
- # You can also extend ThinkingSphinx::Deltas::DefaultDelta to implement
223
+ #
224
+ # You can also extend ThinkingSphinx::Deltas::DefaultDelta to implement
230
225
  # your own handling for delta indexing.
231
- #
226
+ #
232
227
  def set_property(*args)
233
228
  options = args.extract_options!
234
229
  options.each do |key, value|
235
230
  set_single_property key, value
236
231
  end
237
-
232
+
238
233
  set_single_property args[0], args[1] if args.length == 2
239
234
  end
240
235
  alias_method :set_properties, :set_property
241
-
236
+
242
237
  # Handles the generation of new columns for the field and attribute
243
238
  # definitions.
244
- #
239
+ #
245
240
  def method_missing(method, *args)
246
241
  FauxColumn.new(method, *args)
247
242
  end
248
-
243
+
249
244
  # A method to allow adding fields from associations which have names
250
245
  # that clash with method names in the Builder class (ie: properties,
251
246
  # fields, attributes).
252
- #
247
+ #
253
248
  # Example: indexes assoc(:properties).column
254
- #
249
+ #
255
250
  def assoc(assoc, *args)
256
251
  FauxColumn.new(assoc, *args)
257
252
  end
258
-
259
- # Use this method to generate SQL for your attributes, conditions, etc.
260
- # You can pass in as whatever ActiveRecord::Base.sanitize_sql accepts.
261
- #
262
- # where sanitize_sql(["active = ?", true])
263
- # #=> WHERE active = 1
264
- #
265
- def sanitize_sql(*args)
266
- @index.model.send(:sanitize_sql, *args)
267
- end
268
-
253
+
269
254
  private
270
-
255
+
271
256
  def source
272
257
  @source ||= begin
273
258
  source = ThinkingSphinx::Source.new(@index)
@@ -275,7 +260,7 @@ module ThinkingSphinx
275
260
  source
276
261
  end
277
262
  end
278
-
263
+
279
264
  def set_single_property(key, value)
280
265
  source_options = ThinkingSphinx::Configuration::SourceOptions
281
266
  if source_options.include?(key.to_s)
@@ -284,19 +269,19 @@ module ThinkingSphinx
284
269
  @index.local_options.merge! key => value
285
270
  end
286
271
  end
287
-
272
+
288
273
  def add_sort_attribute(field, options)
289
274
  add_internal_attribute field, options, "_sort"
290
275
  end
291
-
276
+
292
277
  def add_facet_attribute(property, options)
293
278
  add_internal_attribute property, options, "_facet", true
294
279
  @index.model.sphinx_facets << property.to_facet
295
280
  end
296
-
281
+
297
282
  def add_internal_attribute(property, options, suffix, crc = false)
298
283
  return unless ThinkingSphinx::Facet.translate?(property)
299
-
284
+
300
285
  Attribute.new(source,
301
286
  property.columns.collect { |col| col.clone },
302
287
  options.merge(
@@ -306,7 +291,7 @@ module ThinkingSphinx
306
291
  ).except(:facet)
307
292
  )
308
293
  end
309
-
294
+
310
295
  def no_fields?
311
296
  @index.sources.empty? || @index.sources.any? { |source|
312
297
  source.fields.length == 0