thinking-sphinx 2.0.4 → 2.0.5
Sign up to get free protection for your applications and to get access to all the features.
- 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
|