thinking-sphinx 2.0.4 → 2.0.5
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.
- data/README.textile +2 -0
- data/VERSION +1 -1
- data/features/searching_by_model.feature +13 -0
- data/features/support/env.rb +2 -1
- data/features/thinking_sphinx/db/fixtures/cats.rb +1 -1
- data/features/thinking_sphinx/db/fixtures/dogs.rb +1 -1
- data/features/thinking_sphinx/db/fixtures/foxes.rb +1 -1
- data/features/thinking_sphinx/db/fixtures/posts.rb +5 -1
- data/features/thinking_sphinx/db/migrations/create_posts.rb +1 -0
- data/features/thinking_sphinx/models/developer.rb +1 -0
- data/features/thinking_sphinx/models/music.rb +3 -1
- data/features/thinking_sphinx/models/post.rb +1 -0
- data/lib/thinking_sphinx.rb +2 -2
- data/lib/thinking_sphinx/active_record.rb +32 -2
- data/lib/thinking_sphinx/active_record/delta.rb +0 -27
- data/lib/thinking_sphinx/adapters/mysql_adapter.rb +4 -0
- data/lib/thinking_sphinx/adapters/postgresql_adapter.rb +4 -0
- data/lib/thinking_sphinx/association.rb +55 -5
- data/lib/thinking_sphinx/attribute.rb +17 -10
- data/lib/thinking_sphinx/auto_version.rb +1 -1
- data/lib/thinking_sphinx/class_facet.rb +7 -3
- data/lib/thinking_sphinx/configuration.rb +3 -3
- data/lib/thinking_sphinx/facet.rb +1 -0
- data/lib/thinking_sphinx/facet_search.rb +5 -1
- data/lib/thinking_sphinx/field.rb +16 -0
- data/lib/thinking_sphinx/search.rb +21 -5
- data/lib/thinking_sphinx/sinatra.rb +7 -0
- data/lib/thinking_sphinx/source.rb +33 -2
- data/lib/thinking_sphinx/source/internal_properties.rb +8 -3
- data/lib/thinking_sphinx/source/sql.rb +10 -1
- data/spec/thinking_sphinx/attribute_spec.rb +86 -94
- data/spec/thinking_sphinx/auto_version_spec.rb +8 -0
- data/spec/thinking_sphinx/facet_search_spec.rb +12 -6
- data/spec/thinking_sphinx/index/builder_spec.rb +32 -29
- data/spec/thinking_sphinx/search_spec.rb +14 -11
- metadata +5 -4
data/README.textile
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.0.
|
1
|
+
2.0.5
|
@@ -66,6 +66,12 @@ Feature: Searching on a single model
|
|
66
66
|
When I filter by 2001-01-01 on comments_created_at
|
67
67
|
Then I should get 1 result
|
68
68
|
|
69
|
+
Scenario: Filtering on a wordcount attribute
|
70
|
+
Given Sphinx is running
|
71
|
+
And I am searching on developers
|
72
|
+
When I filter between 0 and 1 on state_wordcount
|
73
|
+
Then I should get 5 results
|
74
|
+
|
69
75
|
Scenario: Searching by NULL/0 values in MVAs
|
70
76
|
Given Sphinx is running
|
71
77
|
And I am searching on boxes
|
@@ -166,3 +172,10 @@ Feature: Searching on a single model
|
|
166
172
|
And I am searching on posts
|
167
173
|
When I search for "Shakespeare"
|
168
174
|
Then I should get 1 result
|
175
|
+
|
176
|
+
Scenario: Searching on content from file field
|
177
|
+
Given Sphinx is running
|
178
|
+
And I am searching on posts
|
179
|
+
When I search for "foo bar baz"
|
180
|
+
Then I should get 1 result
|
181
|
+
|
data/features/support/env.rb
CHANGED
@@ -9,13 +9,14 @@ Dir[File.join(File.dirname(__FILE__), '../../vendor/*/lib')].each do |path|
|
|
9
9
|
$:.unshift path
|
10
10
|
end
|
11
11
|
|
12
|
+
require 'active_support/core_ext/class/inheritable_attributes'
|
12
13
|
require 'active_record'
|
13
14
|
require 'cucumber/thinking_sphinx/internal_world'
|
14
15
|
|
15
16
|
world = Cucumber::ThinkingSphinx::InternalWorld.new
|
16
17
|
world.configure_database
|
17
18
|
|
18
|
-
require
|
19
|
+
require 'thinking_sphinx'
|
19
20
|
|
20
21
|
ActiveRecord::Base.send(:include, ThinkingSphinx::ActiveRecord)
|
21
22
|
|
@@ -1,5 +1,9 @@
|
|
1
1
|
post = Post.create(
|
2
|
-
:subject => "Hello World",
|
2
|
+
:subject => "Hello World",
|
3
|
+
:content => "Um Text",
|
4
|
+
:id => 1,
|
5
|
+
:category_id => 1,
|
6
|
+
:keywords_file => (File.dirname(__FILE__) + '/post_keywords.txt')
|
3
7
|
)
|
4
8
|
|
5
9
|
post.authors << Author.find(:first)
|
@@ -11,6 +11,7 @@ class Post < ActiveRecord::Base
|
|
11
11
|
indexes tags.text, :as => :tags
|
12
12
|
indexes comments.content, :as => :comments
|
13
13
|
indexes authors.name, :as => :authors
|
14
|
+
indexes keywords_file, :as => :keywords, :file => true
|
14
15
|
|
15
16
|
has comments(:id), :as => :comment_ids, :source => :ranged_query,
|
16
17
|
:facet => true
|
data/lib/thinking_sphinx.rb
CHANGED
@@ -102,8 +102,8 @@ module ThinkingSphinx
|
|
102
102
|
end
|
103
103
|
end
|
104
104
|
|
105
|
-
def self.unique_id_expression(offset = nil)
|
106
|
-
"* #{context.indexed_models.size} + #{offset || 0}"
|
105
|
+
def self.unique_id_expression(adapter, offset = nil)
|
106
|
+
"* #{adapter.cast_to_int context.indexed_models.size} + #{offset || 0}"
|
107
107
|
end
|
108
108
|
|
109
109
|
# Check if index definition is disabled.
|
@@ -13,7 +13,11 @@ module ThinkingSphinx
|
|
13
13
|
module ActiveRecord
|
14
14
|
def self.included(base)
|
15
15
|
base.class_eval do
|
16
|
-
|
16
|
+
if defined?(class_attribute)
|
17
|
+
class_attribute :sphinx_indexes, :sphinx_facets
|
18
|
+
else
|
19
|
+
class_inheritable_array :sphinx_indexes, :sphinx_facets
|
20
|
+
end
|
17
21
|
|
18
22
|
extend ThinkingSphinx::ActiveRecord::ClassMethods
|
19
23
|
|
@@ -202,7 +206,6 @@ module ThinkingSphinx
|
|
202
206
|
|
203
207
|
def insert_sphinx_index(index)
|
204
208
|
self.sphinx_indexes << index
|
205
|
-
descendants.each { |klass| klass.insert_sphinx_index(index) }
|
206
209
|
end
|
207
210
|
|
208
211
|
def has_sphinx_indexes?
|
@@ -272,6 +275,33 @@ module ThinkingSphinx
|
|
272
275
|
index eldest_indexed_ancestor
|
273
276
|
end
|
274
277
|
|
278
|
+
# Temporarily disable delta indexing inside a block, then perform a
|
279
|
+
# single rebuild of index at the end.
|
280
|
+
#
|
281
|
+
# Useful when performing updates to batches of models to prevent
|
282
|
+
# the delta index being rebuilt after each individual update.
|
283
|
+
#
|
284
|
+
# In the following example, the delta index will only be rebuilt
|
285
|
+
# once, not 10 times.
|
286
|
+
#
|
287
|
+
# SomeModel.suspended_delta do
|
288
|
+
# 10.times do
|
289
|
+
# SomeModel.create( ... )
|
290
|
+
# end
|
291
|
+
# end
|
292
|
+
#
|
293
|
+
def suspended_delta(reindex_after = true, &block)
|
294
|
+
define_indexes
|
295
|
+
original_setting = ThinkingSphinx.deltas_suspended?
|
296
|
+
ThinkingSphinx.deltas_suspended = true
|
297
|
+
begin
|
298
|
+
yield
|
299
|
+
ensure
|
300
|
+
ThinkingSphinx.deltas_suspended = original_setting
|
301
|
+
self.index_delta if reindex_after
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
275
305
|
private
|
276
306
|
|
277
307
|
def local_sphinx_indexes
|
@@ -22,33 +22,6 @@ module ThinkingSphinx
|
|
22
22
|
def delta_objects
|
23
23
|
self.sphinx_indexes.collect(&:delta_object).compact
|
24
24
|
end
|
25
|
-
|
26
|
-
# Temporarily disable delta indexing inside a block, then perform a
|
27
|
-
# single rebuild of index at the end.
|
28
|
-
#
|
29
|
-
# Useful when performing updates to batches of models to prevent
|
30
|
-
# the delta index being rebuilt after each individual update.
|
31
|
-
#
|
32
|
-
# In the following example, the delta index will only be rebuilt
|
33
|
-
# once, not 10 times.
|
34
|
-
#
|
35
|
-
# SomeModel.suspended_delta do
|
36
|
-
# 10.times do
|
37
|
-
# SomeModel.create( ... )
|
38
|
-
# end
|
39
|
-
# end
|
40
|
-
#
|
41
|
-
def suspended_delta(reindex_after = true, &block)
|
42
|
-
define_indexes
|
43
|
-
original_setting = ThinkingSphinx.deltas_suspended?
|
44
|
-
ThinkingSphinx.deltas_suspended = true
|
45
|
-
begin
|
46
|
-
yield
|
47
|
-
ensure
|
48
|
-
ThinkingSphinx.deltas_suspended = original_setting
|
49
|
-
self.index_delta if reindex_after
|
50
|
-
end
|
51
|
-
end
|
52
25
|
end
|
53
26
|
|
54
27
|
def toggled_delta?
|
@@ -58,16 +58,14 @@ module ThinkingSphinx
|
|
58
58
|
def join_to(base_join)
|
59
59
|
parent.join_to(base_join) if parent && parent.join.nil?
|
60
60
|
|
61
|
-
@join ||=
|
62
|
-
@reflection, base_join, parent ? parent.join : base_join
|
61
|
+
@join ||= join_association_class.new(
|
62
|
+
@reflection, base_join, parent ? parent.join : join_parent(base_join)
|
63
63
|
)
|
64
64
|
end
|
65
65
|
|
66
66
|
def arel_join
|
67
67
|
@join.join_type = Arel::OuterJoin
|
68
|
-
|
69
|
-
"#{@reflection.klass.connection.quote_table_name(@join.parent.aliased_table_name)}"
|
70
|
-
) if @join.options[:conditions].is_a?(String)
|
68
|
+
rewrite_conditions
|
71
69
|
|
72
70
|
@join
|
73
71
|
end
|
@@ -165,5 +163,57 @@ module ThinkingSphinx
|
|
165
163
|
|
166
164
|
options
|
167
165
|
end
|
166
|
+
|
167
|
+
def join_association_class
|
168
|
+
if rails_3_1?
|
169
|
+
::ActiveRecord::Associations::JoinDependency::JoinAssociation
|
170
|
+
else
|
171
|
+
::ActiveRecord::Associations::ClassMethods::JoinDependency::JoinAssociation
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def join_parent(join)
|
176
|
+
if rails_3_1?
|
177
|
+
join.join_parts.first
|
178
|
+
else
|
179
|
+
join.joins.first
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def rails_3_1?
|
184
|
+
::ActiveRecord::Associations.constants.include?(:JoinDependency) ||
|
185
|
+
::ActiveRecord::Associations.constants.include?('JoinDependency')
|
186
|
+
end
|
187
|
+
|
188
|
+
def rewrite_conditions
|
189
|
+
@join.options[:conditions] = case @join.options[:conditions]
|
190
|
+
when String
|
191
|
+
rewrite_condition @join.options[:conditions]
|
192
|
+
when Array
|
193
|
+
@join.options[:conditions].collect { |condition|
|
194
|
+
rewrite_condition condition
|
195
|
+
}
|
196
|
+
else
|
197
|
+
@join.options[:conditions]
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
def rewrite_condition(condition)
|
202
|
+
return condition unless condition.is_a?(String)
|
203
|
+
|
204
|
+
if defined?(ActsAsTaggableOn) &&
|
205
|
+
@reflection.klass == ActsAsTaggableOn::Tagging &&
|
206
|
+
@reflection.name.to_s[/_taggings$/]
|
207
|
+
condition = condition.gsub /taggings\./, "#{quoted_alias @join}."
|
208
|
+
end
|
209
|
+
|
210
|
+
condition.gsub /::ts_join_alias::/, quoted_alias(@join.parent)
|
211
|
+
end
|
212
|
+
|
213
|
+
def quoted_alias(join)
|
214
|
+
@reflection.klass.connection.quote_table_name(
|
215
|
+
join.aliased_table_name
|
216
|
+
)
|
217
|
+
end
|
168
218
|
end
|
169
219
|
end
|
@@ -11,6 +11,21 @@ module ThinkingSphinx
|
|
11
11
|
class Attribute < ThinkingSphinx::Property
|
12
12
|
attr_accessor :query_source
|
13
13
|
|
14
|
+
SphinxTypeMappings = {
|
15
|
+
:multi => :sql_attr_multi,
|
16
|
+
:datetime => :sql_attr_timestamp,
|
17
|
+
:string => :sql_attr_str2ordinal,
|
18
|
+
:float => :sql_attr_float,
|
19
|
+
:boolean => :sql_attr_bool,
|
20
|
+
:integer => :sql_attr_uint,
|
21
|
+
:bigint => :sql_attr_bigint,
|
22
|
+
:wordcount => :sql_attr_str2wordcount
|
23
|
+
}
|
24
|
+
|
25
|
+
if Riddle.loaded_version.to_i > 1
|
26
|
+
SphinxTypeMappings[:string] = :sql_attr_string
|
27
|
+
end
|
28
|
+
|
14
29
|
# To create a new attribute, you'll need to pass in either a single Column
|
15
30
|
# or an array of them, and some (optional) options.
|
16
31
|
#
|
@@ -117,15 +132,7 @@ module ThinkingSphinx
|
|
117
132
|
end
|
118
133
|
|
119
134
|
def type_to_config
|
120
|
-
|
121
|
-
:multi => :sql_attr_multi,
|
122
|
-
:datetime => :sql_attr_timestamp,
|
123
|
-
:string => :sql_attr_str2ordinal,
|
124
|
-
:float => :sql_attr_float,
|
125
|
-
:boolean => :sql_attr_bool,
|
126
|
-
:integer => :sql_attr_uint,
|
127
|
-
:bigint => :sql_attr_bigint
|
128
|
-
}[type]
|
135
|
+
SphinxTypeMappings[type]
|
129
136
|
end
|
130
137
|
|
131
138
|
def include_as_association?
|
@@ -231,7 +238,7 @@ module ThinkingSphinx
|
|
231
238
|
on(*join.association_join)
|
232
239
|
end
|
233
240
|
|
234
|
-
relation = relation.project "#{foreign_key_for_mva base_assoc} #{ThinkingSphinx.unique_id_expression(offset)} AS #{quote_column('id')}, #{primary_key_for_mva(end_assoc)} AS #{quote_column(unique_name)}"
|
241
|
+
relation = relation.project "#{foreign_key_for_mva base_assoc} #{ThinkingSphinx.unique_id_expression(adapter, offset)} AS #{quote_column('id')}, #{primary_key_for_mva(end_assoc)} AS #{quote_column(unique_name)}"
|
235
242
|
|
236
243
|
relation.to_sql
|
237
244
|
end
|
@@ -5,12 +5,16 @@ module ThinkingSphinx
|
|
5
5
|
end
|
6
6
|
|
7
7
|
def attribute_name
|
8
|
-
|
8
|
+
Riddle.loaded_version.to_i < 2 ? 'class_crc' : 'sphinx_internal_class'
|
9
9
|
end
|
10
10
|
|
11
11
|
def value(object, attribute_hash)
|
12
|
-
|
13
|
-
|
12
|
+
if Riddle.loaded_version.to_i < 2
|
13
|
+
crc = attribute_hash['class_crc']
|
14
|
+
ThinkingSphinx::Configuration.instance.models_by_crc[crc]
|
15
|
+
else
|
16
|
+
attribute_hash['sphinx_internal_class']
|
17
|
+
end
|
14
18
|
end
|
15
19
|
end
|
16
20
|
end
|
@@ -91,9 +91,9 @@ module ThinkingSphinx
|
|
91
91
|
if custom_app_root
|
92
92
|
self.app_root = custom_app_root
|
93
93
|
else
|
94
|
-
self.app_root = Rails.root if defined?(Rails)
|
95
94
|
self.app_root = Merb.root if defined?(Merb)
|
96
95
|
self.app_root = Sinatra::Application.root if defined?(Sinatra)
|
96
|
+
self.app_root = Rails.root if defined?(Rails)
|
97
97
|
self.app_root ||= app_root
|
98
98
|
end
|
99
99
|
|
@@ -132,10 +132,10 @@ module ThinkingSphinx
|
|
132
132
|
ThinkingSphinx.mutex.synchronize do
|
133
133
|
@@environment ||= if defined?(Merb)
|
134
134
|
Merb.environment
|
135
|
-
elsif defined?(Sinatra)
|
136
|
-
Sinatra::Application.environment.to_s
|
137
135
|
elsif defined?(Rails)
|
138
136
|
Rails.env
|
137
|
+
elsif defined?(Sinatra)
|
138
|
+
Sinatra::Application.environment.to_s
|
139
139
|
else
|
140
140
|
ENV['RAILS_ENV'] || 'development'
|
141
141
|
end
|
@@ -29,7 +29,7 @@ module ThinkingSphinx
|
|
29
29
|
names = options[:all_facets] ?
|
30
30
|
facet_names_for_all_classes : facet_names_common_to_all_classes
|
31
31
|
|
32
|
-
names.delete
|
32
|
+
names.delete class_facet unless options[:class_facet]
|
33
33
|
names
|
34
34
|
end
|
35
35
|
end
|
@@ -162,5 +162,9 @@ module ThinkingSphinx
|
|
162
162
|
facet.name == name
|
163
163
|
}
|
164
164
|
end
|
165
|
+
|
166
|
+
def class_facet
|
167
|
+
Riddle.loaded_version.to_i < 2 ? 'class_crc' : 'sphinx_internal_class'
|
168
|
+
end
|
165
169
|
end
|
166
170
|
end
|