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.
- checksums.yaml +4 -4
- data/HISTORY +13 -0
- data/README.textile +5 -3
- data/lib/thinking_sphinx.rb +3 -0
- data/lib/thinking_sphinx/active_record/callbacks/update_callbacks.rb +3 -1
- data/lib/thinking_sphinx/active_record/database_adapters/mysql_adapter.rb +4 -0
- data/lib/thinking_sphinx/active_record/database_adapters/postgresql_adapter.rb +4 -0
- data/lib/thinking_sphinx/active_record/property_query.rb +3 -3
- data/lib/thinking_sphinx/active_record/property_sql_presenter.rb +7 -1
- data/lib/thinking_sphinx/active_record/sql_builder.rb +4 -36
- data/lib/thinking_sphinx/active_record/sql_builder/query.rb +6 -0
- data/lib/thinking_sphinx/active_record/sql_builder/statement.rb +30 -0
- data/lib/thinking_sphinx/active_record/sql_source.rb +6 -6
- data/lib/thinking_sphinx/configuration.rb +2 -0
- data/lib/thinking_sphinx/configuration/minimum_fields.rb +31 -0
- data/lib/thinking_sphinx/connection.rb +3 -2
- data/lib/thinking_sphinx/deletion.rb +1 -5
- data/lib/thinking_sphinx/deltas/delete_job.rb +1 -1
- data/lib/thinking_sphinx/excerpter.rb +3 -5
- data/lib/thinking_sphinx/masks/scopes_mask.rb +2 -1
- data/lib/thinking_sphinx/middlewares/sphinxql.rb +14 -1
- data/lib/thinking_sphinx/rake_interface.rb +5 -18
- data/lib/thinking_sphinx/real_time.rb +1 -0
- data/lib/thinking_sphinx/real_time/index.rb +5 -1
- data/lib/thinking_sphinx/real_time/interpreter.rb +4 -0
- data/lib/thinking_sphinx/real_time/populator.rb +48 -0
- data/lib/thinking_sphinx/search/query.rb +1 -1
- data/lib/thinking_sphinx/subscribers/populator_subscriber.rb +34 -0
- data/spec/acceptance/attribute_access_spec.rb +2 -2
- data/spec/acceptance/index_options_spec.rb +1 -1
- data/spec/acceptance/searching_with_filters_spec.rb +20 -0
- data/spec/acceptance/sphinx_scopes_spec.rb +9 -0
- data/spec/internal/app/indices/article_index.rb +1 -1
- data/spec/internal/app/models/book.rb +1 -0
- data/spec/internal/app/models/city.rb +1 -1
- data/spec/internal/db/schema.rb +1 -2
- data/spec/thinking_sphinx/active_record/callbacks/update_callbacks_spec.rb +3 -1
- data/spec/thinking_sphinx/active_record/property_sql_presenter_spec.rb +39 -2
- data/spec/thinking_sphinx/active_record/sql_builder_spec.rb +2 -1
- data/spec/thinking_sphinx/deletion_spec.rb +1 -4
- data/spec/thinking_sphinx/deltas/default_delta_spec.rb +1 -3
- data/spec/thinking_sphinx/excerpter_spec.rb +1 -1
- data/spec/thinking_sphinx/middlewares/sphinxql_spec.rb +29 -0
- data/spec/thinking_sphinx/real_time/index_spec.rb +16 -0
- data/spec/thinking_sphinx/real_time/interpreter_spec.rb +8 -0
- data/spec/thinking_sphinx/search/query_spec.rb +18 -0
- data/thinking-sphinx.gemspec +2 -2
- metadata +7 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 66940af3da082d3b4d69667b40b1b6495f226dd1
|
4
|
+
data.tar.gz: 52af0d5ae485c7caec6fab3206b28c34764ab1a5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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.
|
data/README.textile
CHANGED
@@ -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.
|
10
|
-
gem 'thinking-sphinx', '3.0.
|
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/
|
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.
|
data/lib/thinking_sphinx.rb
CHANGED
@@ -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.
|
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
|
@@ -31,7 +31,7 @@ class ThinkingSphinx::ActiveRecord::PropertyQuery
|
|
31
31
|
def base_association_class
|
32
32
|
base_association.klass
|
33
33
|
end
|
34
|
-
delegate :
|
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
|
-
|
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 =
|
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
|
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,
|
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
|
111
|
-
@sql_query_range
|
112
|
-
@sql_query_info
|
113
|
-
@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
|
11
|
-
:port
|
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.
|
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.
|
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
|
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
|
@@ -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
|
-
|
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.
|
23
|
-
|
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
|
@@ -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 ==
|
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,
|
31
|
+
expectations = [[gods, 2500]]
|
32
32
|
search.each_with_weight do |result, weight|
|
33
33
|
expectation = expectations.shift
|
34
34
|
|
@@ -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
|
data/spec/internal/db/schema.rb
CHANGED
@@ -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
|
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
|
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
|
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
|
@@ -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
|
|
data/thinking-sphinx.gemspec
CHANGED
@@ -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.
|
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.
|
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
|
+
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-
|
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.
|
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.
|
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
|