thinking-sphinx 3.0.4 → 3.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/HISTORY +13 -0
  3. data/README.textile +5 -3
  4. data/lib/thinking_sphinx.rb +3 -0
  5. data/lib/thinking_sphinx/active_record/callbacks/update_callbacks.rb +3 -1
  6. data/lib/thinking_sphinx/active_record/database_adapters/mysql_adapter.rb +4 -0
  7. data/lib/thinking_sphinx/active_record/database_adapters/postgresql_adapter.rb +4 -0
  8. data/lib/thinking_sphinx/active_record/property_query.rb +3 -3
  9. data/lib/thinking_sphinx/active_record/property_sql_presenter.rb +7 -1
  10. data/lib/thinking_sphinx/active_record/sql_builder.rb +4 -36
  11. data/lib/thinking_sphinx/active_record/sql_builder/query.rb +6 -0
  12. data/lib/thinking_sphinx/active_record/sql_builder/statement.rb +30 -0
  13. data/lib/thinking_sphinx/active_record/sql_source.rb +6 -6
  14. data/lib/thinking_sphinx/configuration.rb +2 -0
  15. data/lib/thinking_sphinx/configuration/minimum_fields.rb +31 -0
  16. data/lib/thinking_sphinx/connection.rb +3 -2
  17. data/lib/thinking_sphinx/deletion.rb +1 -5
  18. data/lib/thinking_sphinx/deltas/delete_job.rb +1 -1
  19. data/lib/thinking_sphinx/excerpter.rb +3 -5
  20. data/lib/thinking_sphinx/masks/scopes_mask.rb +2 -1
  21. data/lib/thinking_sphinx/middlewares/sphinxql.rb +14 -1
  22. data/lib/thinking_sphinx/rake_interface.rb +5 -18
  23. data/lib/thinking_sphinx/real_time.rb +1 -0
  24. data/lib/thinking_sphinx/real_time/index.rb +5 -1
  25. data/lib/thinking_sphinx/real_time/interpreter.rb +4 -0
  26. data/lib/thinking_sphinx/real_time/populator.rb +48 -0
  27. data/lib/thinking_sphinx/search/query.rb +1 -1
  28. data/lib/thinking_sphinx/subscribers/populator_subscriber.rb +34 -0
  29. data/spec/acceptance/attribute_access_spec.rb +2 -2
  30. data/spec/acceptance/index_options_spec.rb +1 -1
  31. data/spec/acceptance/searching_with_filters_spec.rb +20 -0
  32. data/spec/acceptance/sphinx_scopes_spec.rb +9 -0
  33. data/spec/internal/app/indices/article_index.rb +1 -1
  34. data/spec/internal/app/models/book.rb +1 -0
  35. data/spec/internal/app/models/city.rb +1 -1
  36. data/spec/internal/db/schema.rb +1 -2
  37. data/spec/thinking_sphinx/active_record/callbacks/update_callbacks_spec.rb +3 -1
  38. data/spec/thinking_sphinx/active_record/property_sql_presenter_spec.rb +39 -2
  39. data/spec/thinking_sphinx/active_record/sql_builder_spec.rb +2 -1
  40. data/spec/thinking_sphinx/deletion_spec.rb +1 -4
  41. data/spec/thinking_sphinx/deltas/default_delta_spec.rb +1 -3
  42. data/spec/thinking_sphinx/excerpter_spec.rb +1 -1
  43. data/spec/thinking_sphinx/middlewares/sphinxql_spec.rb +29 -0
  44. data/spec/thinking_sphinx/real_time/index_spec.rb +16 -0
  45. data/spec/thinking_sphinx/real_time/interpreter_spec.rb +8 -0
  46. data/spec/thinking_sphinx/search/query_spec.rb +18 -0
  47. data/thinking-sphinx.gemspec +2 -2
  48. metadata +7 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e4abfc7a0ce650e92e51a842336ec1028f12e9ea
4
- data.tar.gz: 2c51520fcd002486f51e127f981f5e7c60000706
3
+ metadata.gz: 66940af3da082d3b4d69667b40b1b6495f226dd1
4
+ data.tar.gz: 52af0d5ae485c7caec6fab3206b28c34764ab1a5
5
5
  SHA512:
6
- metadata.gz: 384ba739298c2dc12431cf9c74e79a3f15191098d84b41afab0e94383ac9403384e9290e38c834ef17a1675eedf5835477f63c12aeeb440144421dab97e82959
7
- data.tar.gz: 787f2a640087f91d7ffd7d39d1b092fb027551e6df58d2ab19062a78bfebe3b7bbbfe8ad4c65858d84fc590403eb0515296c6a5192fc3aaf6937e2f595b1ec3a
6
+ metadata.gz: eb3099e4ef3987b78b20003ba8c7ebaa13846fae26cdb6c384f47962d584dd144e511d8dcaa81f9900a9d18d3020edc489a1fde377a1816ec0337ba6b3da0ce8
7
+ data.tar.gz: b43d0cb3be98bd7d738f4b8bf18492832e496a7c441368fb250ef8ecbfb4d13a5ebb66c6a14ce57b74d44cbcc6131b96f2a1581342852b7d576ccb6ffd75743c
data/HISTORY CHANGED
@@ -1,3 +1,16 @@
1
+ 2013-08-26: 3.0.5
2
+ * [CHANGE] Updating Riddle dependency to be >= 1.5.8.
3
+ * [FEATURE] Allow scoping of real-time index models.
4
+ * [CHANGE] Real-time index population presentation and logic are now separated.
5
+ * [CHANGE] Using the connection pool for update callbacks, excerpts, deletions.
6
+ * [FIX] Respect existing sql_query_range/sql_query_info settings.
7
+ * [CHANGE] Don't add the sphinx_internal_class_name unless STI models are indexed.
8
+ * [FIX] Don't add select clauses or joins to sql_query if they're for query/ranged-query properties.
9
+ * [CHANGE] Use Mysql2's reconnect option and have it turned on by default.
10
+ * [FIX] Set database timezones as part of the indexing process.
11
+ * [CHANGE] Improved auto-starring with escaped characters.
12
+ * [FIX] Chaining scopes with just options works again.
13
+
1
14
  2013-07-09: 3.0.4
2
15
  * [CHANGE] Updating Riddle dependency to be >= 1.5.7.
3
16
  * [FEATURE] ts:regenerate rake task for rebuilding Sphinx when realtime indices are involved.
@@ -6,8 +6,8 @@ h2. Installation
6
6
 
7
7
  It's a gem, so install it like you would any other gem. You will also need to specify the Mysql2 gem as well (this is not an inbuilt dependency because JRuby, when supported, will need something different):
8
8
 
9
- <pre><code>gem 'mysql2', '0.3.12b4'
10
- gem 'thinking-sphinx', '3.0.0'</code></pre>
9
+ <pre><code>gem 'mysql2', '0.3.13'
10
+ gem 'thinking-sphinx', '3.0.5'</code></pre>
11
11
 
12
12
  The mysql2 gem is required for connecting to Sphinx, so please include it even when you're using PostgreSQL for your database.
13
13
 
@@ -39,6 +39,8 @@ Other changes:
39
39
  * SphinxQL is now used instead of the old socket connections (hence the dependency on the @mysql2@ gem).
40
40
  * Specifying a different port for Sphinx to use (in @config/thinking_sphinx.yml@) should be done with the mysql41 setting, not the port setting.
41
41
  * The searchd_log and searchd_query_log settings are now log and query_log (matching their Sphinx names).
42
+ * searchd_file_path is now indices_location.
43
+ * config_file is now configuration_file.
42
44
  * You'll need to include @ThinkingSphinx::Scopes@ into your models if you want to use Sphinx scopes. Default scopes can be set as follows:
43
45
 
44
46
  <pre><code>class Person < ActiveRecord::Base
@@ -130,7 +132,7 @@ batch.searches #=> [[foo results], [bar results], [baz results]]</code></pre>
130
132
  * To search on specific indices, use the @:indices@ option, which expects an array of index names (including the @_core@ or @_delta@ suffixes).
131
133
  * @:without_any@ has become @:without_all@ - and is implemented, but Sphinx doesn't yet support the required logic.
132
134
  * If you're creating a multi-value attribute manually (using a SQL snippet), then in the definition pass in @:multi => true@, but @:type@ should be set as well, to one of the MVA types that Sphinx supports (@:integer@, @:timestamp@, or @:boolean@).
133
- * Automatic updates of non-string attributes are still limited to those from columns on the model in question, and is disabled by default. To enable it, just set attribute_updates to true in your @config/sphinx.yml@.
135
+ * Automatic updates of non-string attributes are still limited to those from columns on the model in question, and is disabled by default. To enable it, just set attribute_updates to true in your @config/thinking_sphinx.yml@.
134
136
  * Search result helper methods are no longer injected into the actual result objects. Read the section below on search results, glazes and panes.
135
137
  * If you're using string facets, make sure they're defined as fields, not strings. There is currently no support for multi-value string facets.
136
138
  * To have fine-grained control over when deltas are invoked, create a sub-class of your chosen delta class (the standard is @ThinkingSphinx::Deltas::DefaultDelta@) and customise the @toggle@ and @toggled?@ methods, both of which accept a single parameter being the ActiveRecord instance.
@@ -34,6 +34,8 @@ module ThinkingSphinx
34
34
  end
35
35
 
36
36
  @before_index_hooks = []
37
+
38
+ module Subscribers; end
37
39
  end
38
40
 
39
41
  # Core
@@ -56,6 +58,7 @@ require 'thinking_sphinx/panes'
56
58
  require 'thinking_sphinx/rake_interface'
57
59
  require 'thinking_sphinx/scopes'
58
60
  require 'thinking_sphinx/search'
61
+ require 'thinking_sphinx/subscribers/populator_subscriber'
59
62
  require 'thinking_sphinx/test'
60
63
  # Extended
61
64
  require 'thinking_sphinx/active_record'
@@ -42,7 +42,9 @@ class ThinkingSphinx::ActiveRecord::Callbacks::UpdateCallbacks <
42
42
  sphinxql = Riddle::Query.update(
43
43
  index.name, index.document_id_for_key(instance.id), attributes
44
44
  )
45
- ThinkingSphinx::Connection.new.execute(sphinxql)
45
+ ThinkingSphinx::Connection.take do |connection|
46
+ connection.execute(sphinxql)
47
+ end
46
48
  rescue Mysql2::Error => error
47
49
  # This isn't vital, so don't raise the error.
48
50
  end
@@ -25,6 +25,10 @@ class ThinkingSphinx::ActiveRecord::DatabaseAdapters::MySQLAdapter <
25
25
  "GROUP_CONCAT(#{clause} SEPARATOR '#{separator}')"
26
26
  end
27
27
 
28
+ def time_zone_query_pre
29
+ ["SET TIME_ZONE = '+0:00'"]
30
+ end
31
+
28
32
  def utf8_query_pre
29
33
  ['SET NAMES utf8']
30
34
  end
@@ -30,4 +30,8 @@ class ThinkingSphinx::ActiveRecord::DatabaseAdapters::PostgreSQLAdapter <
30
30
  def group_concatenate(clause, separator = ' ')
31
31
  "array_to_string(array_agg(#{clause}), '#{separator}')"
32
32
  end
33
+
34
+ def time_zone_query_pre
35
+ ['SET TIME ZONE UTC']
36
+ end
33
37
  end
@@ -31,7 +31,7 @@ class ThinkingSphinx::ActiveRecord::PropertyQuery
31
31
  def base_association_class
32
32
  base_association.klass
33
33
  end
34
- delegate :relation, :to => :base_association_class, :prefix => true
34
+ delegate :unscoped, :to => :base_association_class, :prefix => true
35
35
 
36
36
  def column
37
37
  @column ||= property.columns.first
@@ -93,7 +93,7 @@ class ThinkingSphinx::ActiveRecord::PropertyQuery
93
93
  end
94
94
 
95
95
  def range_sql
96
- base_association_class_relation.select(
96
+ base_association_class_unscoped.select(
97
97
  "MIN(#{quoted_foreign_key}), MAX(#{quoted_foreign_key})"
98
98
  ).to_sql
99
99
  end
@@ -105,7 +105,7 @@ class ThinkingSphinx::ActiveRecord::PropertyQuery
105
105
  def to_sql
106
106
  raise "Could not determine SQL for MVA" if reflections.empty?
107
107
 
108
- relation = base_association_class_relation.select("#{quoted_foreign_key} #{offset} AS #{quote_column('id')}, #{quoted_primary_key} AS #{quote_column(property.name)}"
108
+ relation = base_association_class_unscoped.select("#{quoted_foreign_key} #{offset} AS #{quote_column('id')}, #{quoted_primary_key} AS #{quote_column(property.name)}"
109
109
  )
110
110
  relation = relation.joins(joins) if joins.present?
111
111
  relation = relation.where("#{quoted_foreign_key} BETWEEN $start AND $end") if ranged?
@@ -6,12 +6,14 @@ class ThinkingSphinx::ActiveRecord::PropertySQLPresenter
6
6
  end
7
7
 
8
8
  def to_group
9
- return nil unless group?
9
+ return nil if sourced_by_query? || !group?
10
10
 
11
11
  columns_with_table
12
12
  end
13
13
 
14
14
  def to_select
15
+ return nil if sourced_by_query?
16
+
15
17
  "#{casted_column_with_table} AS #{adapter.quote property.name}"
16
18
  end
17
19
 
@@ -76,4 +78,8 @@ class ThinkingSphinx::ActiveRecord::PropertySQLPresenter
76
78
  def group?
77
79
  !(aggregate? || property.columns.any?(&:string?))
78
80
  end
81
+
82
+ def sourced_by_query?
83
+ property.source_type.to_s[/query/]
84
+ end
79
85
  end
@@ -26,6 +26,10 @@ module ThinkingSphinx
26
26
 
27
27
  private
28
28
 
29
+ delegate :adapter, :model, :delta_processor, :to => :source
30
+ delegate :convert_nulls, :time_zone_query_pre, :utf8_query_pre,
31
+ :to => :adapter
32
+
29
33
  def query
30
34
  Query.new(self)
31
35
  end
@@ -38,16 +42,10 @@ module ThinkingSphinx
38
42
  ThinkingSphinx::Configuration.instance
39
43
  end
40
44
 
41
- delegate :adapter, :model, :delta_processor, :to => :source
42
- delegate :convert_nulls, :utf8_query_pre, :to => :adapter
43
45
  def relation
44
46
  model.unscoped
45
47
  end
46
48
 
47
- def base_join
48
- @base_join ||= join_dependency_class.new model, [], initial_joins
49
- end
50
-
51
49
  def associations
52
50
  @associations ||= ThinkingSphinx::ActiveRecord::Associations.new(model).tap do |assocs|
53
51
  source.associations.reject(&:string?).each do |association|
@@ -56,10 +54,6 @@ module ThinkingSphinx
56
54
  end
57
55
  end
58
56
 
59
- def custom_joins
60
- @custom_joins ||= source.associations.select(&:string?).collect(&:to_s)
61
- end
62
-
63
57
  def quote_column(column)
64
58
  model.connection.quote_column_name(column)
65
59
  end
@@ -85,24 +79,6 @@ module ThinkingSphinx
85
79
  "($id - #{source.offset}) / #{config.indices.count}"
86
80
  end
87
81
 
88
- def attribute_presenters
89
- @attribute_presenters ||= property_sql_presenters_for(source.attributes)
90
- end
91
-
92
- def field_presenters
93
- @field_presenters ||= property_sql_presenters_for(source.fields)
94
- end
95
-
96
- def property_sql_presenters_for(fields)
97
- fields.collect { |field| property_sql_presenter_for(field) }
98
- end
99
-
100
- def property_sql_presenter_for(field)
101
- ThinkingSphinx::ActiveRecord::PropertySQLPresenter.new(
102
- field, source.adapter, associations
103
- )
104
- end
105
-
106
82
  def inheritance_column_condition
107
83
  "#{quoted_inheritance_column} = '#{model_name}'"
108
84
  end
@@ -114,14 +90,6 @@ module ThinkingSphinx
114
90
  condition
115
91
  end
116
92
 
117
- def presenters_to_group(presenters)
118
- presenters.collect(&:to_group)
119
- end
120
-
121
- def presenters_to_select(presenters)
122
- presenters.collect(&:to_select)
123
- end
124
-
125
93
  def groupings
126
94
  groupings = source.groupings
127
95
  if model.column_names.include?(model.inheritance_column)
@@ -13,9 +13,11 @@ module ThinkingSphinx
13
13
  end
14
14
 
15
15
  protected
16
+
16
17
  attr_accessor :report, :scope
17
18
 
18
19
  def filter_by_query_pre
20
+ scope_by_time_zone
19
21
  scope_by_delta_processor
20
22
  scope_by_session
21
23
  scope_by_utf8
@@ -31,6 +33,10 @@ module ThinkingSphinx
31
33
  end
32
34
  end
33
35
 
36
+ def scope_by_time_zone
37
+ self.scope += time_zone_query_pre
38
+ end
39
+
34
40
  def scope_by_utf8
35
41
  self.scope += utf8_query_pre if source.options[:utf8?]
36
42
  end
@@ -35,6 +35,10 @@ module ThinkingSphinx
35
35
  protected
36
36
  attr_accessor :report, :scope
37
37
 
38
+ def custom_joins
39
+ @custom_joins ||= source.associations.select(&:string?).collect(&:to_s)
40
+ end
41
+
38
42
  def filter_by_query_range
39
43
  minimum = convert_nulls "MIN(#{quoted_primary_key})", 1
40
44
  maximum = convert_nulls "MAX(#{quoted_primary_key})", 1
@@ -55,6 +59,32 @@ module ThinkingSphinx
55
59
  scope_by_order
56
60
  end
57
61
 
62
+ def attribute_presenters
63
+ @attribute_presenters ||= property_sql_presenters_for source.attributes
64
+ end
65
+
66
+ def field_presenters
67
+ @field_presenters ||= property_sql_presenters_for source.fields
68
+ end
69
+
70
+ def presenters_to_group(presenters)
71
+ presenters.collect(&:to_group)
72
+ end
73
+
74
+ def presenters_to_select(presenters)
75
+ presenters.collect(&:to_select)
76
+ end
77
+
78
+ def property_sql_presenters_for(properties)
79
+ properties.collect { |property| property_sql_presenter_for(property) }
80
+ end
81
+
82
+ def property_sql_presenter_for(property)
83
+ ThinkingSphinx::ActiveRecord::PropertySQLPresenter.new(
84
+ property, source.adapter, associations
85
+ )
86
+ end
87
+
58
88
  def scope_by_select
59
89
  self.scope = scope.select(pre_select + select_clause)
60
90
  end
@@ -3,8 +3,8 @@ module ThinkingSphinx
3
3
  class SQLSource < Riddle::Configuration::SQLSource
4
4
  include ThinkingSphinx::Core::Settings
5
5
  attr_reader :model, :database_settings, :options
6
- attr_accessor :fields, :attributes, :associations, :conditions, :groupings,
7
- :polymorphs
6
+ attr_accessor :fields, :attributes, :associations, :conditions,
7
+ :groupings, :polymorphs
8
8
 
9
9
  OPTIONS = [:name, :offset, :delta_processor, :delta?, :disable_range?,
10
10
  :group_concat_max_len, :utf8?, :position]
@@ -107,10 +107,10 @@ module ThinkingSphinx
107
107
  end
108
108
 
109
109
  def build_sql_query
110
- @sql_query = builder.sql_query
111
- @sql_query_range = builder.sql_query_range
112
- @sql_query_info = builder.sql_query_info
113
- @sql_query_pre += builder.sql_query_pre
110
+ @sql_query = builder.sql_query
111
+ @sql_query_range ||= builder.sql_query_range
112
+ @sql_query_info ||= builder.sql_query_info
113
+ @sql_query_pre += builder.sql_query_pre
114
114
  end
115
115
 
116
116
  def config
@@ -80,6 +80,7 @@ class ThinkingSphinx::Configuration < Riddle::Configuration
80
80
  preload_indices
81
81
 
82
82
  ThinkingSphinx::Configuration::ConsistentIds.new(indices).reconcile
83
+ ThinkingSphinx::Configuration::MinimumFields.new(indices).reconcile
83
84
 
84
85
  super
85
86
  end
@@ -159,3 +160,4 @@ end
159
160
 
160
161
  require 'thinking_sphinx/configuration/consistent_ids'
161
162
  require 'thinking_sphinx/configuration/defaults'
163
+ require 'thinking_sphinx/configuration/minimum_fields'
@@ -0,0 +1,31 @@
1
+ class ThinkingSphinx::Configuration::MinimumFields
2
+ def initialize(indices)
3
+ @indices = indices
4
+ end
5
+
6
+ def reconcile
7
+ return unless no_inheritance_columns?
8
+
9
+ sources.each do |source|
10
+ source.fields.delete_if do |field|
11
+ field.name == 'sphinx_internal_class_name'
12
+ end
13
+ end
14
+ end
15
+
16
+ private
17
+
18
+ attr_reader :indices
19
+
20
+ def no_inheritance_columns?
21
+ indices.select { |index|
22
+ index.model.column_names.include?(index.model.inheritance_column)
23
+ }.empty?
24
+ end
25
+
26
+ def sources
27
+ @sources ||= @indices.select { |index|
28
+ index.respond_to?(:sources)
29
+ }.collect(&:sources).flatten
30
+ end
31
+ end
@@ -7,8 +7,9 @@ module ThinkingSphinx::Connection
7
7
  address = '127.0.0.1' if address == 'localhost'
8
8
 
9
9
  options = {
10
- :host => address,
11
- :port => configuration.searchd.mysql41
10
+ :host => address,
11
+ :port => configuration.searchd.mysql41,
12
+ :reconnect => true
12
13
  }.merge(configuration.settings['connection_options'] || {})
13
14
 
14
15
  connection_class.new address, options[:port], options
@@ -18,16 +18,12 @@ class ThinkingSphinx::Deletion
18
18
 
19
19
  attr_reader :index, :instance
20
20
 
21
- def connection
22
- @connection ||= ThinkingSphinx::Connection.new
23
- end
24
-
25
21
  def document_id_for_key
26
22
  index.document_id_for_key instance.id
27
23
  end
28
24
 
29
25
  def execute(statement)
30
- ThinkingSphinx::Connection.pool.take do |connection|
26
+ ThinkingSphinx::Connection.take do |connection|
31
27
  connection.execute statement
32
28
  end
33
29
  end
@@ -4,7 +4,7 @@ class ThinkingSphinx::Deltas::DeleteJob
4
4
  end
5
5
 
6
6
  def perform
7
- ThinkingSphinx::Connection.pool.take do |connection|
7
+ ThinkingSphinx::Connection.take do |connection|
8
8
  connection.execute Riddle::Query.update(
9
9
  @index_name, @document_id, :sphinx_deleted => true
10
10
  )
@@ -14,7 +14,9 @@ class ThinkingSphinx::Excerpter
14
14
  end
15
15
 
16
16
  def excerpt!(text)
17
- result = connection.query(statement_for(text)).first['snippet']
17
+ result = ThinkingSphinx::Connection.take do |connection|
18
+ connection.query(statement_for(text)).first['snippet']
19
+ end
18
20
 
19
21
  result.encode!("ISO-8859-1")
20
22
  result.force_encoding("UTF-8")
@@ -22,10 +24,6 @@ class ThinkingSphinx::Excerpter
22
24
 
23
25
  private
24
26
 
25
- def connection
26
- @connection ||= ThinkingSphinx::Connection.new
27
- end
28
-
29
27
  def statement_for(text)
30
28
  Riddle::Query.snippets(text, index, words, options)
31
29
  end
@@ -22,7 +22,8 @@ class ThinkingSphinx::Masks::ScopesMask
22
22
  private
23
23
 
24
24
  def apply_scope(scope, *args)
25
- search *sphinx_scopes[scope].call(*args)
25
+ query, options = sphinx_scopes[scope].call(*args)
26
+ search query, options
26
27
  end
27
28
 
28
29
  def can_apply_scope?(scope)
@@ -55,6 +55,10 @@ class ThinkingSphinx::Middlewares::SphinxQL <
55
55
  "(#{classes_and_descendants_names.join('|')})"
56
56
  end
57
57
 
58
+ def class_condition_required?
59
+ classes.any? && !indices_match_classes?
60
+ end
61
+
58
62
  def constantize_inheritance_column(klass)
59
63
  klass.connection.select_values(inheritance_column_select(klass)).compact.each(&:constantize)
60
64
  end
@@ -70,6 +74,12 @@ class ThinkingSphinx::Middlewares::SphinxQL <
70
74
  end.flatten
71
75
  end
72
76
 
77
+ def indices_match_classes?
78
+ indices.collect(&:reference).uniq == classes.collect { |klass|
79
+ klass.name.underscore.to_sym
80
+ }
81
+ end
82
+
73
83
  def inheritance_column_select(klass)
74
84
  <<-SQL
75
85
  SELECT DISTINCT #{klass.inheritance_column}
@@ -85,7 +95,10 @@ SQL
85
95
 
86
96
  def extended_query
87
97
  conditions = options[:conditions] || {}
88
- conditions[:sphinx_internal_class_name] = class_condition if classes.any?
98
+ if class_condition_required?
99
+ conditions[:sphinx_internal_class_name] = class_condition
100
+ end
101
+
89
102
  @extended_query ||= ThinkingSphinx::Search::Query.new(
90
103
  context.search.query, conditions, options[:star]
91
104
  ).to_s
@@ -19,20 +19,9 @@ class ThinkingSphinx::RakeInterface
19
19
 
20
20
  FileUtils.mkdir_p configuration.indices_location
21
21
 
22
- configuration.indices.each do |index|
23
- next unless index.is_a?(ThinkingSphinx::RealTime::Index)
24
-
25
- puts "Generating index files for #{index.name}"
26
- transcriber = ThinkingSphinx::RealTime::Transcriber.new index
27
- Dir["#{index.path}*"].each { |file| FileUtils.rm file }
28
-
29
- index.model.find_each do |instance|
30
- transcriber.copy instance
31
- print "."
32
- end
33
- print "\n"
34
-
35
- controller.rotate
22
+ indices = configuration.indices.select { |index| index.type == 'rt' }
23
+ indices.each do |index|
24
+ ThinkingSphinx::RealTime::Populator.populate index
36
25
  end
37
26
  end
38
27
 
@@ -71,11 +60,9 @@ class ThinkingSphinx::RakeInterface
71
60
 
72
61
  private
73
62
 
63
+ delegate :controller, :to => :configuration
64
+
74
65
  def configuration
75
66
  ThinkingSphinx::Configuration.instance
76
67
  end
77
-
78
- def controller
79
- configuration.controller
80
- end
81
68
  end
@@ -13,6 +13,7 @@ require 'thinking_sphinx/real_time/attribute'
13
13
  require 'thinking_sphinx/real_time/field'
14
14
  require 'thinking_sphinx/real_time/index'
15
15
  require 'thinking_sphinx/real_time/interpreter'
16
+ require 'thinking_sphinx/real_time/populator'
16
17
  require 'thinking_sphinx/real_time/transcriber'
17
18
 
18
19
  require 'thinking_sphinx/real_time/callbacks/real_time_callbacks'
@@ -1,7 +1,7 @@
1
1
  class ThinkingSphinx::RealTime::Index < Riddle::Configuration::RealtimeIndex
2
2
  include ThinkingSphinx::Core::Index
3
3
 
4
- attr_writer :fields, :attributes, :conditions
4
+ attr_writer :fields, :attributes, :conditions, :scope
5
5
 
6
6
  def initialize(reference, options = {})
7
7
  @fields = []
@@ -43,6 +43,10 @@ class ThinkingSphinx::RealTime::Index < Riddle::Configuration::RealtimeIndex
43
43
  @fields
44
44
  end
45
45
 
46
+ def scope
47
+ @scope.nil? ? model : @scope.call
48
+ end
49
+
46
50
  def unique_attribute_names
47
51
  attributes.collect(&:name)
48
52
  end
@@ -15,6 +15,10 @@ class ThinkingSphinx::RealTime::Interpreter <
15
15
  }
16
16
  end
17
17
 
18
+ def scope(&block)
19
+ @index.scope = block
20
+ end
21
+
18
22
  def set_property(properties)
19
23
  properties.each do |key, value|
20
24
  @index.send("#{key}=", value) if @index.class.settings.include?(key)
@@ -0,0 +1,48 @@
1
+ class ThinkingSphinx::RealTime::Populator
2
+ def self.populate(index)
3
+ new(index).populate
4
+ end
5
+
6
+ def initialize(index)
7
+ @index = index
8
+ end
9
+
10
+ def populate(&block)
11
+ instrument 'start_populating'
12
+
13
+ remove_files
14
+
15
+ scope.find_each do |instance|
16
+ transcriber.copy instance
17
+ instrument 'populated', :instance => instance
18
+ end
19
+
20
+ controller.rotate
21
+ instrument 'finish_populating'
22
+ end
23
+
24
+ private
25
+
26
+ attr_reader :index
27
+
28
+ delegate :controller, :to => :configuration
29
+ delegate :scope, :to => :index
30
+
31
+ def configuration
32
+ ThinkingSphinx::Configuration.instance
33
+ end
34
+
35
+ def instrument(message, options = {})
36
+ ActiveSupport::Notifications.instrument(
37
+ "#{message}.thinking_sphinx.real_time", options.merge(:index => index)
38
+ )
39
+ end
40
+
41
+ def remove_files
42
+ Dir["#{index.path}*"].each { |file| FileUtils.rm file }
43
+ end
44
+
45
+ def transcriber
46
+ @transcriber ||= ThinkingSphinx::RealTime::Transcriber.new index
47
+ end
48
+ end
@@ -28,7 +28,7 @@ class ThinkingSphinx::Search::Query
28
28
  keyword.gsub(/("#{token}(.*?#{token})?"|(?![!-])#{token})/u) do
29
29
  pre, proper, post = $`, $&, $'
30
30
  # E.g. "@foo", "/2", "~3", but not as part of a token
31
- is_operator = pre.match(%r{(\W|^)[@~/]\Z}) ||
31
+ is_operator = pre.match(%r{\A(\W|^)[@~/]\Z}) ||
32
32
  pre.match(%r{(\W|^)@\([^\)]*$})
33
33
  # E.g. "foo bar", with quotes
34
34
  is_quote = proper[/^".*"$/]
@@ -0,0 +1,34 @@
1
+ class ThinkingSphinx::Subscribers::PopulatorSubscriber
2
+ def self.attach_to(namespace)
3
+ subscriber = new
4
+
5
+ subscriber.public_methods(false).each do |event|
6
+ next if event == :call
7
+
8
+ ActiveSupport::Notifications.subscribe(
9
+ "#{event}.#{namespace}", subscriber
10
+ )
11
+ end
12
+ end
13
+
14
+ def call(message, *args)
15
+ send message.split('.').first,
16
+ ActiveSupport::Notifications::Event.new(message, *args)
17
+ end
18
+
19
+ def start_populating(event)
20
+ puts "Generating index files for #{event.payload[:index].name}"
21
+ end
22
+
23
+ def populated(event)
24
+ print '.'
25
+ end
26
+
27
+ def finish_populating(event)
28
+ print "\n"
29
+ end
30
+ end
31
+
32
+ ThinkingSphinx::Subscribers::PopulatorSubscriber.attach_to(
33
+ 'thinking_sphinx.real_time'
34
+ )
@@ -18,7 +18,7 @@ describe 'Accessing attributes directly via search results', :live => true do
18
18
  search = Book.search('gods', :select => '*, @weight')
19
19
  search.context[:panes] << ThinkingSphinx::Panes::WeightPane
20
20
 
21
- search.first.weight.should == 3500
21
+ search.first.weight.should == 2500
22
22
  end
23
23
 
24
24
  it "can enumerate with the weight" do
@@ -28,7 +28,7 @@ describe 'Accessing attributes directly via search results', :live => true do
28
28
  search = Book.search('gods', :select => '*, @weight')
29
29
  search.masks << ThinkingSphinx::Masks::WeightEnumeratorMask
30
30
 
31
- expectations = [[gods, 3500]]
31
+ expectations = [[gods, 2500]]
32
32
  search.each_with_weight do |result, weight|
33
33
  expectation = expectations.shift
34
34
 
@@ -106,7 +106,7 @@ describe 'Index options' do
106
106
  end
107
107
 
108
108
  it "respects sql_query_pre values" do
109
- index.sources.first.sql_query_pre.should == ["DO STUFF"]
109
+ index.sources.first.sql_query_pre.should include("DO STUFF")
110
110
  end
111
111
  end
112
112
 
@@ -69,6 +69,26 @@ describe 'Searching with filters', :live => true do
69
69
  ).to_a.should == [pancakes]
70
70
  end
71
71
 
72
+ it "takes into account local timezones for timestamps" do
73
+ pancakes = Article.create :title => 'Pancakes'
74
+ waffles = Article.create :title => 'Waffles'
75
+
76
+ food = Tag.create :name => 'food'
77
+ flat = Tag.create :name => 'flat'
78
+
79
+ Tagging.create(:tag => food, :article => pancakes).
80
+ update_column :created_at, 5.minutes.ago
81
+ Tagging.create :tag => flat, :article => pancakes
82
+ Tagging.create(:tag => food, :article => waffles).
83
+ update_column :created_at, 3.minute.ago
84
+
85
+ index
86
+
87
+ Article.search(
88
+ :with => {:taggings_at => 2.minutes.ago..Time.zone.now}
89
+ ).to_a.should == [pancakes]
90
+ end
91
+
72
92
  it "limits results with MVAs having all of the given values" do
73
93
  pancakes = Article.create :title => 'Pancakes'
74
94
  waffles = Article.create :title => 'Waffles'
@@ -25,6 +25,15 @@ describe 'Sphinx scopes', :live => true do
25
25
  grave = Book.create! :title => 'The Graveyard Book', :year => 2009
26
26
  index
27
27
 
28
+ Book.by_year(2001..2005).ordered.to_a.should == [boys, gods]
29
+ end
30
+
31
+ it "allows chaining of scopes that include queries" do
32
+ gods = Book.create! :title => 'American Gods', :year => 2001
33
+ boys = Book.create! :title => 'Anansi Boys', :year => 2005
34
+ grave = Book.create! :title => 'The Graveyard Book', :year => 2009
35
+ index
36
+
28
37
  Book.by_year(2001).by_query_and_year('Graveyard', 2009).to_a.
29
38
  should == [grave]
30
39
  end
@@ -4,7 +4,7 @@ ThinkingSphinx::Index.define :article, :with => :active_record do
4
4
  indexes user.articles.title, :as => :related_titles
5
5
 
6
6
  has published, user_id
7
- has taggings.tag_id, :as => :tag_ids
7
+ has taggings.tag_id, :as => :tag_ids, :source => :query
8
8
  has taggings.created_at, :as => :taggings_at
9
9
 
10
10
  set_property :min_infix_len => 4
@@ -10,4 +10,5 @@ class Book < ActiveRecord::Base
10
10
  sphinx_scope(:by_query_and_year) do |query, year|
11
11
  [query, {:with => {:year =>year}}]
12
12
  end
13
+ sphinx_scope(:ordered) { {:order => 'year DESC'} }
13
14
  end
@@ -1,3 +1,3 @@
1
1
  class City < ActiveRecord::Base
2
- scope :ordered, order(:name)
2
+ scope :ordered, lambda { order(:name) }
3
3
  end
@@ -33,8 +33,7 @@ ActiveRecord::Schema.define do
33
33
  end
34
34
 
35
35
  create_table(:categories, :force => true) do |t|
36
- t.integer :id
37
- t.string :name
36
+ t.string :name
38
37
  end
39
38
 
40
39
  create_table(:categorisations, :force => true) do |t|
@@ -26,9 +26,11 @@ describe ThinkingSphinx::ActiveRecord::Callbacks::UpdateCallbacks do
26
26
  before :each do
27
27
  stub_const 'ThinkingSphinx::Configuration',
28
28
  double(:instance => configuration)
29
- stub_const 'ThinkingSphinx::Connection', double(:new => connection)
29
+ stub_const 'ThinkingSphinx::Connection', double
30
30
  stub_const 'Riddle::Query', double(:update => 'SphinxQL')
31
31
 
32
+ ThinkingSphinx::Connection.stub(:take).and_yield(connection)
33
+
32
34
  source.attributes.replace([
33
35
  double(:name => 'foo', :updateable? => true,
34
36
  :columns => [double(:__name => 'foo_column')]),
@@ -17,7 +17,7 @@ describe ThinkingSphinx::ActiveRecord::PropertySQLPresenter do
17
17
  )
18
18
  }
19
19
  let(:field) { double('field', :name => 'title', :columns => [column],
20
- :type => nil, :multi? => false) }
20
+ :type => nil, :multi? => false, :source_type => nil) }
21
21
  let(:column) { double('column', :string? => false, :__stack => [],
22
22
  :__name => 'title') }
23
23
 
@@ -40,6 +40,12 @@ describe ThinkingSphinx::ActiveRecord::PropertySQLPresenter do
40
40
 
41
41
  presenter.to_group.should be_nil
42
42
  end
43
+
44
+ it "returns nil if the field is sourced via a separate query" do
45
+ field.stub :source_type => 'query'
46
+
47
+ presenter.to_group.should be_nil
48
+ end
43
49
  end
44
50
 
45
51
  describe '#to_select' do
@@ -95,6 +101,18 @@ describe ThinkingSphinx::ActiveRecord::PropertySQLPresenter do
95
101
  presenter.to_select.
96
102
  should == "CONCAT_WS(' ', articles.title) AS title"
97
103
  end
104
+
105
+ it "returns nil for query sourced fields" do
106
+ field.stub :source_type => :query
107
+
108
+ presenter.to_select.should be_nil
109
+ end
110
+
111
+ it "returns nil for ranged query sourced fields" do
112
+ field.stub :source_type => :ranged_query
113
+
114
+ presenter.to_select.should be_nil
115
+ end
98
116
  end
99
117
  end
100
118
 
@@ -105,7 +123,8 @@ describe ThinkingSphinx::ActiveRecord::PropertySQLPresenter do
105
123
  )
106
124
  }
107
125
  let(:attribute) { double('attribute', :name => 'created_at',
108
- :columns => [column], :type => :integer, :multi? => false) }
126
+ :columns => [column], :type => :integer, :multi? => false,
127
+ :source_type => nil) }
109
128
  let(:column) { double('column', :string? => false, :__stack => [],
110
129
  :__name => 'created_at') }
111
130
 
@@ -140,6 +159,12 @@ describe ThinkingSphinx::ActiveRecord::PropertySQLPresenter do
140
159
 
141
160
  presenter.to_group.should be_nil
142
161
  end
162
+
163
+ it "returns nil if the attribute is sourced via a separate query" do
164
+ attribute.stub :source_type => 'query'
165
+
166
+ presenter.to_group.should be_nil
167
+ end
143
168
  end
144
169
 
145
170
  describe '#to_select' do
@@ -196,6 +221,18 @@ describe ThinkingSphinx::ActiveRecord::PropertySQLPresenter do
196
221
 
197
222
  presenter.to_select.should == "CONCAT_WS(',', CAST(articles.created_at AS varchar), CAST(articles.created_at AS varchar)) AS created_at"
198
223
  end
224
+
225
+ it "returns nil for query sourced attributes" do
226
+ attribute.stub :source_type => :query
227
+
228
+ presenter.to_select.should be_nil
229
+ end
230
+
231
+ it "returns nil for ranged query sourced attributes" do
232
+ attribute.stub :source_type => :ranged_query
233
+
234
+ presenter.to_select.should be_nil
235
+ end
199
236
  end
200
237
  end
201
238
  end
@@ -15,7 +15,8 @@ describe ThinkingSphinx::ActiveRecord::SQLBuilder do
15
15
  let(:indices) { double('indices', :count => 5) }
16
16
  let(:presenter) { double('presenter', :to_select => '`name` AS `name`',
17
17
  :to_group => '`name`') }
18
- let(:adapter) { double('adapter') }
18
+ let(:adapter) { double('adapter',
19
+ :time_zone_query_pre => ['SET TIME ZONE']) }
19
20
  let(:associations) { double('associations', :join_values => []) }
20
21
  let(:builder) { ThinkingSphinx::ActiveRecord::SQLBuilder.new source }
21
22
 
@@ -6,13 +6,10 @@ describe ThinkingSphinx::Deletion do
6
6
  let(:index) { double('index', :name => 'foo_core',
7
7
  :document_id_for_key => 14, :type => 'plain') }
8
8
  let(:instance) { double('instance', :id => 7) }
9
- let(:pool) { double 'pool' }
10
9
 
11
10
  before :each do
12
- ThinkingSphinx::Connection.stub :pool => pool
11
+ ThinkingSphinx::Connection.stub(:take).and_yield(connection)
13
12
  Riddle::Query.stub :update => 'UPDATE STATEMENT'
14
-
15
- pool.stub(:take).and_yield(connection)
16
13
  end
17
14
 
18
15
  context 'index is SQL-backed' do
@@ -23,12 +23,10 @@ describe ThinkingSphinx::Deltas::DefaultDelta do
23
23
  let(:index) { double('index', :name => 'foo_core',
24
24
  :document_id_for_key => 14) }
25
25
  let(:instance) { double('instance', :id => 7) }
26
- let(:pool) { double }
27
26
 
28
27
  before :each do
29
- ThinkingSphinx::Connection.stub :pool => pool
28
+ ThinkingSphinx::Connection.stub(:take).and_yield(connection)
30
29
  Riddle::Query.stub :update => 'UPDATE STATEMENT'
31
- pool.stub(:take).and_yield(connection)
32
30
  end
33
31
 
34
32
  it "updates the deleted flag to false" do
@@ -7,7 +7,7 @@ describe ThinkingSphinx::Excerpter do
7
7
  }
8
8
 
9
9
  before :each do
10
- ThinkingSphinx::Connection.stub :new => connection
10
+ ThinkingSphinx::Connection.stub(:take).and_yield(connection)
11
11
  Riddle::Query.stub :snippets => 'CALL SNIPPETS'
12
12
  end
13
13
 
@@ -6,6 +6,7 @@ module ActiveRecord
6
6
  class Base; end
7
7
  end
8
8
 
9
+ require 'active_support/core_ext/module/delegation'
9
10
  require 'active_support/core_ext/object/blank'
10
11
  require 'active_support/core_ext/string/inflections'
11
12
  require 'thinking_sphinx/middlewares/middleware'
@@ -49,6 +50,7 @@ describe ThinkingSphinx::Middlewares::SphinxQL do
49
50
  :name => 'User')
50
51
  search.options[:classes] = [klass]
51
52
  search.options[:indices] = ['user_core']
53
+ index_set.first.stub :reference => :user
52
54
 
53
55
  ThinkingSphinx::IndexSet.should_receive(:new).
54
56
  with([klass], ['user_core']).and_return(index_set)
@@ -83,6 +85,30 @@ describe ThinkingSphinx::Middlewares::SphinxQL do
83
85
  middleware.call [context]
84
86
  end
85
87
 
88
+ it "doesn't append a field condition by default" do
89
+ ThinkingSphinx::Search::Query.should_receive(:new) do |query, conditions, star|
90
+ conditions[:sphinx_internal_class_name].should be_nil
91
+ query
92
+ end
93
+
94
+ middleware.call [context]
95
+ end
96
+
97
+ it "doesn't append a field condition if all classes match index references" do
98
+ model = double('model', :connection => double,
99
+ :ancestors => [ActiveRecord::Base], :name => 'Animal')
100
+ index_set.first.stub :reference => :animal
101
+
102
+ search.options[:classes] = [model]
103
+
104
+ ThinkingSphinx::Search::Query.should_receive(:new) do |query, conditions, star|
105
+ conditions[:sphinx_internal_class_name].should be_nil
106
+ query
107
+ end
108
+
109
+ middleware.call [context]
110
+ end
111
+
86
112
  it "appends field conditions for the class when searching on subclasses" do
87
113
  db_connection = double('db connection', :select_values => [],
88
114
  :schema_cache => double('cache', :table_exists? => false))
@@ -98,6 +124,7 @@ describe ThinkingSphinx::Middlewares::SphinxQL do
98
124
  end
99
125
  submodel.stub :connection => db_connection, :column_names => ['type'],
100
126
  :descendants => []
127
+ index_set.first.stub :reference => :cat
101
128
 
102
129
  search.options[:classes] = [submodel]
103
130
 
@@ -123,6 +150,7 @@ describe ThinkingSphinx::Middlewares::SphinxQL do
123
150
  end
124
151
  submodel.stub :connection => db_connection, :column_names => ['type'],
125
152
  :descendants => []
153
+ index_set.first.stub :reference => :"animals/cat"
126
154
 
127
155
  search.options[:classes] = [submodel]
128
156
 
@@ -136,6 +164,7 @@ describe ThinkingSphinx::Middlewares::SphinxQL do
136
164
  it "does not query the database for subclasses if :skip_sti is set to true" do
137
165
  model = double('model', :connection => double,
138
166
  :ancestors => [ActiveRecord::Base], :name => 'Animal')
167
+ index_set.first.stub :reference => :animal
139
168
 
140
169
  search.options[:classes] = [model]
141
170
  search.options[:skip_sti] = true
@@ -149,6 +149,22 @@ describe ThinkingSphinx::RealTime::Index do
149
149
  end
150
150
  end
151
151
 
152
+ describe '#scope' do
153
+ let(:model) { double('model') }
154
+
155
+ it "returns the model by default" do
156
+ ActiveSupport::Inflector.stub(:constantize => model)
157
+
158
+ index.scope.should == model
159
+ end
160
+
161
+ it "returns the evaluated scope if provided" do
162
+ index.scope = lambda { :foo }
163
+
164
+ index.scope.should == :foo
165
+ end
166
+ end
167
+
152
168
  describe '#unique_attribute_names' do
153
169
  it "returns all attribute names" do
154
170
  index.unique_attribute_names.should == [
@@ -119,6 +119,14 @@ describe ThinkingSphinx::RealTime::Interpreter do
119
119
  end
120
120
  end
121
121
 
122
+ describe '#scope' do
123
+ it "passes the scope block through to the index" do
124
+ index.should_receive(:scope=).with(instance_of(Proc))
125
+
126
+ instance.scope { :foo }
127
+ end
128
+ end
129
+
122
130
  describe '#set_property' do
123
131
  before :each do
124
132
  index.class.stub :settings => [:morphology]
@@ -69,6 +69,24 @@ describe ThinkingSphinx::Search::Query do
69
69
  query.to_s.should == "*\\@pan*"
70
70
  end
71
71
 
72
+ it "ignores escaped slashes" do
73
+ query = ThinkingSphinx::Search::Query.new "\\/\\/pan", {}, true
74
+
75
+ query.to_s.should == "\\/\\/*pan*"
76
+ end
77
+
78
+ it "does not star quorum operators" do
79
+ query = ThinkingSphinx::Search::Query.new "foo/3", {}, true
80
+
81
+ query.to_s.should == "*foo*/3"
82
+ end
83
+
84
+ it "does not star proximity operators or quoted strings" do
85
+ query = ThinkingSphinx::Search::Query.new %q{"hello world"~3}, {}, true
86
+
87
+ query.to_s.should == %q{"hello world"~3}
88
+ end
89
+
72
90
  it "handles null values by removing them from the conditions hash" do
73
91
  query = ThinkingSphinx::Search::Query.new '', :title => nil
74
92
 
@@ -3,7 +3,7 @@ $:.push File.expand_path('../lib', __FILE__)
3
3
 
4
4
  Gem::Specification.new do |s|
5
5
  s.name = 'thinking-sphinx'
6
- s.version = '3.0.4'
6
+ s.version = '3.0.5'
7
7
  s.platform = Gem::Platform::RUBY
8
8
  s.authors = ["Pat Allan"]
9
9
  s.email = ["pat@freelancing-gods.com"]
@@ -24,7 +24,7 @@ Gem::Specification.new do |s|
24
24
  s.add_runtime_dependency 'builder', '>= 2.1.2'
25
25
  s.add_runtime_dependency 'middleware', '>= 0.1.0'
26
26
  s.add_runtime_dependency 'innertube', '>= 1.0.2'
27
- s.add_runtime_dependency 'riddle', '>= 1.5.7'
27
+ s.add_runtime_dependency 'riddle', '>= 1.5.8'
28
28
 
29
29
  s.add_development_dependency 'appraisal', '~> 0.4.0'
30
30
  s.add_development_dependency 'combustion', '~> 0.4.0'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: thinking-sphinx
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.4
4
+ version: 3.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pat Allan
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-07-09 00:00:00.000000000 Z
11
+ date: 2013-08-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -72,14 +72,14 @@ dependencies:
72
72
  requirements:
73
73
  - - '>='
74
74
  - !ruby/object:Gem::Version
75
- version: 1.5.7
75
+ version: 1.5.8
76
76
  type: :runtime
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - '>='
81
81
  - !ruby/object:Gem::Version
82
- version: 1.5.7
82
+ version: 1.5.8
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: appraisal
85
85
  requirement: !ruby/object:Gem::Requirement
@@ -198,6 +198,7 @@ files:
198
198
  - lib/thinking_sphinx/configuration.rb
199
199
  - lib/thinking_sphinx/configuration/consistent_ids.rb
200
200
  - lib/thinking_sphinx/configuration/defaults.rb
201
+ - lib/thinking_sphinx/configuration/minimum_fields.rb
201
202
  - lib/thinking_sphinx/connection.rb
202
203
  - lib/thinking_sphinx/core.rb
203
204
  - lib/thinking_sphinx/core/field.rb
@@ -249,6 +250,7 @@ files:
249
250
  - lib/thinking_sphinx/real_time/index.rb
250
251
  - lib/thinking_sphinx/real_time/index/template.rb
251
252
  - lib/thinking_sphinx/real_time/interpreter.rb
253
+ - lib/thinking_sphinx/real_time/populator.rb
252
254
  - lib/thinking_sphinx/real_time/property.rb
253
255
  - lib/thinking_sphinx/real_time/transcriber.rb
254
256
  - lib/thinking_sphinx/scopes.rb
@@ -260,6 +262,7 @@ files:
260
262
  - lib/thinking_sphinx/search/query.rb
261
263
  - lib/thinking_sphinx/search/stale_ids_exception.rb
262
264
  - lib/thinking_sphinx/sinatra.rb
265
+ - lib/thinking_sphinx/subscribers/populator_subscriber.rb
263
266
  - lib/thinking_sphinx/tasks.rb
264
267
  - lib/thinking_sphinx/test.rb
265
268
  - sketchpad.rb