friendlyfashion-thinking-sphinx 2.0.13
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/HISTORY +244 -0
- data/LICENCE +20 -0
- data/README.textile +235 -0
- data/features/abstract_inheritance.feature +10 -0
- data/features/alternate_primary_key.feature +27 -0
- data/features/attribute_transformation.feature +22 -0
- data/features/attribute_updates.feature +77 -0
- data/features/deleting_instances.feature +67 -0
- data/features/direct_attributes.feature +11 -0
- data/features/excerpts.feature +21 -0
- data/features/extensible_delta_indexing.feature +9 -0
- data/features/facets.feature +88 -0
- data/features/facets_across_model.feature +29 -0
- data/features/field_sorting.feature +18 -0
- data/features/handling_edits.feature +94 -0
- data/features/retry_stale_indexes.feature +24 -0
- data/features/searching_across_models.feature +20 -0
- data/features/searching_by_index.feature +40 -0
- data/features/searching_by_model.feature +175 -0
- data/features/searching_with_find_arguments.feature +56 -0
- data/features/sphinx_detection.feature +25 -0
- data/features/sphinx_scopes.feature +68 -0
- data/features/step_definitions/alpha_steps.rb +16 -0
- data/features/step_definitions/beta_steps.rb +7 -0
- data/features/step_definitions/common_steps.rb +201 -0
- data/features/step_definitions/extensible_delta_indexing_steps.rb +7 -0
- data/features/step_definitions/facet_steps.rb +96 -0
- data/features/step_definitions/find_arguments_steps.rb +36 -0
- data/features/step_definitions/gamma_steps.rb +15 -0
- data/features/step_definitions/scope_steps.rb +19 -0
- data/features/step_definitions/search_steps.rb +94 -0
- data/features/step_definitions/sphinx_steps.rb +35 -0
- data/features/sti_searching.feature +19 -0
- data/features/support/env.rb +27 -0
- data/features/support/lib/generic_delta_handler.rb +8 -0
- data/features/thinking_sphinx/database.example.yml +3 -0
- data/features/thinking_sphinx/db/.gitignore +1 -0
- data/features/thinking_sphinx/db/fixtures/alphas.rb +8 -0
- data/features/thinking_sphinx/db/fixtures/authors.rb +1 -0
- data/features/thinking_sphinx/db/fixtures/betas.rb +11 -0
- data/features/thinking_sphinx/db/fixtures/boxes.rb +9 -0
- data/features/thinking_sphinx/db/fixtures/categories.rb +1 -0
- data/features/thinking_sphinx/db/fixtures/cats.rb +3 -0
- data/features/thinking_sphinx/db/fixtures/comments.rb +24 -0
- data/features/thinking_sphinx/db/fixtures/developers.rb +31 -0
- data/features/thinking_sphinx/db/fixtures/dogs.rb +3 -0
- data/features/thinking_sphinx/db/fixtures/extensible_betas.rb +10 -0
- data/features/thinking_sphinx/db/fixtures/foxes.rb +3 -0
- data/features/thinking_sphinx/db/fixtures/gammas.rb +10 -0
- data/features/thinking_sphinx/db/fixtures/music.rb +4 -0
- data/features/thinking_sphinx/db/fixtures/people.rb +1001 -0
- data/features/thinking_sphinx/db/fixtures/post_keywords.txt +1 -0
- data/features/thinking_sphinx/db/fixtures/posts.rb +10 -0
- data/features/thinking_sphinx/db/fixtures/robots.rb +8 -0
- data/features/thinking_sphinx/db/fixtures/tags.rb +27 -0
- data/features/thinking_sphinx/db/migrations/create_alphas.rb +8 -0
- data/features/thinking_sphinx/db/migrations/create_animals.rb +5 -0
- data/features/thinking_sphinx/db/migrations/create_authors.rb +3 -0
- data/features/thinking_sphinx/db/migrations/create_authors_posts.rb +6 -0
- data/features/thinking_sphinx/db/migrations/create_betas.rb +5 -0
- data/features/thinking_sphinx/db/migrations/create_boxes.rb +5 -0
- data/features/thinking_sphinx/db/migrations/create_categories.rb +3 -0
- data/features/thinking_sphinx/db/migrations/create_comments.rb +10 -0
- data/features/thinking_sphinx/db/migrations/create_developers.rb +7 -0
- data/features/thinking_sphinx/db/migrations/create_extensible_betas.rb +5 -0
- data/features/thinking_sphinx/db/migrations/create_gammas.rb +3 -0
- data/features/thinking_sphinx/db/migrations/create_genres.rb +3 -0
- data/features/thinking_sphinx/db/migrations/create_music.rb +6 -0
- data/features/thinking_sphinx/db/migrations/create_people.rb +13 -0
- data/features/thinking_sphinx/db/migrations/create_posts.rb +6 -0
- data/features/thinking_sphinx/db/migrations/create_robots.rb +4 -0
- data/features/thinking_sphinx/db/migrations/create_taggings.rb +5 -0
- data/features/thinking_sphinx/db/migrations/create_tags.rb +4 -0
- data/features/thinking_sphinx/models/alpha.rb +23 -0
- data/features/thinking_sphinx/models/andrew.rb +17 -0
- data/features/thinking_sphinx/models/animal.rb +5 -0
- data/features/thinking_sphinx/models/author.rb +3 -0
- data/features/thinking_sphinx/models/beta.rb +13 -0
- data/features/thinking_sphinx/models/box.rb +8 -0
- data/features/thinking_sphinx/models/cat.rb +3 -0
- data/features/thinking_sphinx/models/category.rb +4 -0
- data/features/thinking_sphinx/models/comment.rb +10 -0
- data/features/thinking_sphinx/models/developer.rb +21 -0
- data/features/thinking_sphinx/models/dog.rb +3 -0
- data/features/thinking_sphinx/models/extensible_beta.rb +9 -0
- data/features/thinking_sphinx/models/fox.rb +5 -0
- data/features/thinking_sphinx/models/gamma.rb +5 -0
- data/features/thinking_sphinx/models/genre.rb +3 -0
- data/features/thinking_sphinx/models/medium.rb +5 -0
- data/features/thinking_sphinx/models/music.rb +10 -0
- data/features/thinking_sphinx/models/person.rb +24 -0
- data/features/thinking_sphinx/models/post.rb +22 -0
- data/features/thinking_sphinx/models/robot.rb +12 -0
- data/features/thinking_sphinx/models/tag.rb +3 -0
- data/features/thinking_sphinx/models/tagging.rb +4 -0
- data/lib/cucumber/thinking_sphinx/external_world.rb +12 -0
- data/lib/cucumber/thinking_sphinx/internal_world.rb +137 -0
- data/lib/cucumber/thinking_sphinx/sql_logger.rb +28 -0
- data/lib/thinking-sphinx.rb +1 -0
- data/lib/thinking_sphinx/action_controller.rb +31 -0
- data/lib/thinking_sphinx/active_record/attribute_updates.rb +53 -0
- data/lib/thinking_sphinx/active_record/collection_proxy.rb +47 -0
- data/lib/thinking_sphinx/active_record/collection_proxy_with_scopes.rb +27 -0
- data/lib/thinking_sphinx/active_record/delta.rb +67 -0
- data/lib/thinking_sphinx/active_record/has_many_association.rb +44 -0
- data/lib/thinking_sphinx/active_record/has_many_association_with_scopes.rb +21 -0
- data/lib/thinking_sphinx/active_record/log_subscriber.rb +61 -0
- data/lib/thinking_sphinx/active_record/scopes.rb +110 -0
- data/lib/thinking_sphinx/active_record.rb +386 -0
- data/lib/thinking_sphinx/adapters/abstract_adapter.rb +87 -0
- data/lib/thinking_sphinx/adapters/mysql_adapter.rb +62 -0
- data/lib/thinking_sphinx/adapters/postgresql_adapter.rb +188 -0
- data/lib/thinking_sphinx/association.rb +230 -0
- data/lib/thinking_sphinx/attribute.rb +405 -0
- data/lib/thinking_sphinx/auto_version.rb +40 -0
- data/lib/thinking_sphinx/bundled_search.rb +44 -0
- data/lib/thinking_sphinx/class_facet.rb +20 -0
- data/lib/thinking_sphinx/configuration.rb +375 -0
- data/lib/thinking_sphinx/context.rb +76 -0
- data/lib/thinking_sphinx/core/string.rb +15 -0
- data/lib/thinking_sphinx/deltas/default_delta.rb +62 -0
- data/lib/thinking_sphinx/deltas.rb +28 -0
- data/lib/thinking_sphinx/deploy/capistrano.rb +99 -0
- data/lib/thinking_sphinx/excerpter.rb +23 -0
- data/lib/thinking_sphinx/facet.rb +135 -0
- data/lib/thinking_sphinx/facet_search.rb +170 -0
- data/lib/thinking_sphinx/field.rb +98 -0
- data/lib/thinking_sphinx/index/builder.rb +315 -0
- data/lib/thinking_sphinx/index/faux_column.rb +118 -0
- data/lib/thinking_sphinx/index.rb +159 -0
- data/lib/thinking_sphinx/join.rb +37 -0
- data/lib/thinking_sphinx/property.rb +187 -0
- data/lib/thinking_sphinx/railtie.rb +43 -0
- data/lib/thinking_sphinx/search.rb +1061 -0
- data/lib/thinking_sphinx/search_methods.rb +439 -0
- data/lib/thinking_sphinx/sinatra.rb +7 -0
- data/lib/thinking_sphinx/source/internal_properties.rb +51 -0
- data/lib/thinking_sphinx/source/sql.rb +174 -0
- data/lib/thinking_sphinx/source.rb +194 -0
- data/lib/thinking_sphinx/tasks.rb +142 -0
- data/lib/thinking_sphinx/test.rb +55 -0
- data/lib/thinking_sphinx/version.rb +3 -0
- data/lib/thinking_sphinx.rb +297 -0
- data/spec/fixtures/data.sql +32 -0
- data/spec/fixtures/database.yml.default +3 -0
- data/spec/fixtures/models.rb +164 -0
- data/spec/fixtures/structure.sql +146 -0
- data/spec/spec_helper.rb +61 -0
- data/spec/sphinx_helper.rb +60 -0
- data/spec/support/rails.rb +25 -0
- data/spec/thinking_sphinx/active_record/delta_spec.rb +122 -0
- data/spec/thinking_sphinx/active_record/has_many_association_spec.rb +173 -0
- data/spec/thinking_sphinx/active_record/scopes_spec.rb +176 -0
- data/spec/thinking_sphinx/active_record_spec.rb +573 -0
- data/spec/thinking_sphinx/adapters/abstract_adapter_spec.rb +145 -0
- data/spec/thinking_sphinx/association_spec.rb +250 -0
- data/spec/thinking_sphinx/attribute_spec.rb +552 -0
- data/spec/thinking_sphinx/auto_version_spec.rb +103 -0
- data/spec/thinking_sphinx/configuration_spec.rb +326 -0
- data/spec/thinking_sphinx/context_spec.rb +126 -0
- data/spec/thinking_sphinx/core/array_spec.rb +9 -0
- data/spec/thinking_sphinx/core/string_spec.rb +9 -0
- data/spec/thinking_sphinx/excerpter_spec.rb +49 -0
- data/spec/thinking_sphinx/facet_search_spec.rb +176 -0
- data/spec/thinking_sphinx/facet_spec.rb +359 -0
- data/spec/thinking_sphinx/field_spec.rb +127 -0
- data/spec/thinking_sphinx/index/builder_spec.rb +532 -0
- data/spec/thinking_sphinx/index/faux_column_spec.rb +36 -0
- data/spec/thinking_sphinx/index_spec.rb +189 -0
- data/spec/thinking_sphinx/search_methods_spec.rb +156 -0
- data/spec/thinking_sphinx/search_spec.rb +1455 -0
- data/spec/thinking_sphinx/source_spec.rb +267 -0
- data/spec/thinking_sphinx/test_spec.rb +20 -0
- data/spec/thinking_sphinx_spec.rb +204 -0
- metadata +524 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
module ThinkingSphinx
|
|
2
|
+
module ActiveRecord
|
|
3
|
+
module AttributeUpdates
|
|
4
|
+
def self.included(base)
|
|
5
|
+
base.class_eval do
|
|
6
|
+
after_save :update_attribute_values
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
private
|
|
11
|
+
|
|
12
|
+
def update_attribute_values
|
|
13
|
+
return true unless ThinkingSphinx.updates_enabled? &&
|
|
14
|
+
ThinkingSphinx.sphinx_running?
|
|
15
|
+
|
|
16
|
+
self.class.sphinx_indexes.each do |index|
|
|
17
|
+
attribute_pairs = attribute_values_for_index(index)
|
|
18
|
+
attribute_names = attribute_pairs.keys
|
|
19
|
+
attribute_values = attribute_names.collect { |key|
|
|
20
|
+
attribute_pairs[key]
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
update_index index.core_name, attribute_names, attribute_values
|
|
24
|
+
next unless index.delta?
|
|
25
|
+
update_index index.delta_name, attribute_names, attribute_values
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
true
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def updatable_attributes(index)
|
|
32
|
+
index.attributes.select { |attrib| attrib.updatable? }
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def attribute_values_for_index(index)
|
|
36
|
+
updatable_attributes(index).inject({}) { |hash, attrib|
|
|
37
|
+
hash[attrib.unique_name.to_s] = attrib.live_value self
|
|
38
|
+
hash
|
|
39
|
+
}
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def update_index(index_name, attribute_names, attribute_values)
|
|
43
|
+
config = ThinkingSphinx::Configuration.instance
|
|
44
|
+
config.client.update index_name, attribute_names, {
|
|
45
|
+
sphinx_document_id => attribute_values
|
|
46
|
+
}
|
|
47
|
+
rescue Riddle::ConnectionError, Riddle::ResponseError,
|
|
48
|
+
ThinkingSphinx::SphinxError, Errno::ETIMEDOUT
|
|
49
|
+
# Not the end of the world if Sphinx isn't running.
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
module ThinkingSphinx
|
|
2
|
+
module ActiveRecord
|
|
3
|
+
module CollectionProxy
|
|
4
|
+
def search(*args)
|
|
5
|
+
proxy_association.klass.search(*association_args(args))
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def facets(*args)
|
|
9
|
+
proxy_association.klass.facets(*association_args(args))
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
private
|
|
13
|
+
|
|
14
|
+
def association_args(args)
|
|
15
|
+
options = args.extract_options!
|
|
16
|
+
options[:with] ||= {}
|
|
17
|
+
options[:with].merge! default_filter
|
|
18
|
+
|
|
19
|
+
args + [options]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def attribute_for_foreign_key
|
|
23
|
+
if proxy_association.reflection.through_reflection
|
|
24
|
+
foreign_key = proxy_association.reflection.through_reflection.foreign_key
|
|
25
|
+
else
|
|
26
|
+
foreign_key = proxy_association.reflection.foreign_key
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
proxy_association.klass.define_indexes
|
|
30
|
+
(proxy_association.klass.sphinx_indexes || []).each do |index|
|
|
31
|
+
attribute = index.attributes.detect { |attrib|
|
|
32
|
+
attrib.columns.length == 1 &&
|
|
33
|
+
attrib.columns.first.__name == foreign_key.to_sym ||
|
|
34
|
+
attrib.alias == foreign_key.to_sym
|
|
35
|
+
}
|
|
36
|
+
return attribute unless attribute.nil?
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
raise "Missing Attribute for Foreign Key #{foreign_key}"
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def default_filter
|
|
43
|
+
{attribute_for_foreign_key.unique_name => proxy_association.owner.id}
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module ThinkingSphinx
|
|
2
|
+
module ActiveRecord
|
|
3
|
+
module CollectionProxyWithScopes
|
|
4
|
+
def self.included(base)
|
|
5
|
+
base.class_eval do
|
|
6
|
+
alias_method_chain :method_missing, :sphinx_scopes
|
|
7
|
+
alias_method_chain :respond_to?, :sphinx_scopes
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def method_missing_with_sphinx_scopes(method, *args, &block)
|
|
12
|
+
klass = proxy_association.klass
|
|
13
|
+
if klass.respond_to?(:sphinx_scopes) && klass.sphinx_scopes.include?(method)
|
|
14
|
+
klass.search(:with => default_filter).send(method, *args, &block)
|
|
15
|
+
else
|
|
16
|
+
method_missing_without_sphinx_scopes(method, *args, &block)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def respond_to_with_sphinx_scopes?(method)
|
|
21
|
+
proxy_association.klass.respond_to?(:sphinx_scopes) &&
|
|
22
|
+
proxy_association.klass.sphinx_scopes.include?(method) ||
|
|
23
|
+
respond_to_without_sphinx_scopes?(method)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
module ThinkingSphinx
|
|
2
|
+
module ActiveRecord
|
|
3
|
+
# This module contains all the delta-related code for models. There isn't
|
|
4
|
+
# really anything you need to call manually in here - except perhaps
|
|
5
|
+
# index_delta, but not sure what reason why.
|
|
6
|
+
#
|
|
7
|
+
module Delta
|
|
8
|
+
# Code for after_commit callback is written by Eli Miller:
|
|
9
|
+
# http://elimiller.blogspot.com/2007/06/proper-cache-expiry-with-aftercommit.html
|
|
10
|
+
# with slight modification from Joost Hietbrink.
|
|
11
|
+
#
|
|
12
|
+
def self.included(base)
|
|
13
|
+
base.class_eval do
|
|
14
|
+
class << self
|
|
15
|
+
# Build the delta index for the related model. This won't be called
|
|
16
|
+
# if running in the test environment.
|
|
17
|
+
#
|
|
18
|
+
def index_delta(instance = nil)
|
|
19
|
+
delta_objects.each { |obj| obj.index(self, instance) }
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def delta_objects
|
|
23
|
+
self.sphinx_indexes.collect(&:delta_object).compact
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def toggled_delta?
|
|
28
|
+
self.class.delta_objects.any? { |obj| obj.toggled(self) }
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
# Set the delta value for the model to be true.
|
|
34
|
+
def toggle_delta
|
|
35
|
+
self.class.delta_objects.each { |obj|
|
|
36
|
+
obj.toggle(self)
|
|
37
|
+
} if should_toggle_delta?
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Build the delta index for the related model. This won't be called
|
|
41
|
+
# if running in the test environment.
|
|
42
|
+
#
|
|
43
|
+
def index_delta
|
|
44
|
+
self.class.index_delta(self) if self.class.delta_objects.any? { |obj|
|
|
45
|
+
obj.toggled(self)
|
|
46
|
+
}
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def should_toggle_delta?
|
|
50
|
+
return fire_delta? if respond_to?(:fire_delta?)
|
|
51
|
+
|
|
52
|
+
self.new_record? || indexed_data_changed?
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def indexed_data_changed?
|
|
56
|
+
sphinx_indexes.any? { |index|
|
|
57
|
+
index.fields.any? { |field| field.changed?(self) } ||
|
|
58
|
+
index.attributes.any? { |attrib|
|
|
59
|
+
attrib.public? && attrib.changed?(self) && !attrib.updatable?
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
module ThinkingSphinx
|
|
2
|
+
module ActiveRecord
|
|
3
|
+
module HasManyAssociation
|
|
4
|
+
def search(*args)
|
|
5
|
+
@reflection.klass.search(*association_args(args))
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def facets(*args)
|
|
9
|
+
@reflection.klass.facets(*association_args(args))
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
private
|
|
13
|
+
|
|
14
|
+
def association_args(args)
|
|
15
|
+
options = args.extract_options!
|
|
16
|
+
options[:with] ||= {}
|
|
17
|
+
options[:with].merge! default_filter
|
|
18
|
+
|
|
19
|
+
args + [options]
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def attribute_for_foreign_key
|
|
23
|
+
foreign_key = @reflection.primary_key_name
|
|
24
|
+
stack = [@reflection.options[:through]].compact
|
|
25
|
+
|
|
26
|
+
@reflection.klass.define_indexes
|
|
27
|
+
(@reflection.klass.sphinx_indexes || []).each do |index|
|
|
28
|
+
attribute = index.attributes.detect { |attrib|
|
|
29
|
+
attrib.columns.length == 1 &&
|
|
30
|
+
attrib.columns.first.__name == foreign_key.to_sym ||
|
|
31
|
+
attrib.alias == foreign_key.to_sym
|
|
32
|
+
}
|
|
33
|
+
return attribute unless attribute.nil?
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
raise "Missing Attribute for Foreign Key #{foreign_key}"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def default_filter
|
|
40
|
+
{attribute_for_foreign_key.unique_name => @owner.id}
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
module ThinkingSphinx
|
|
2
|
+
module ActiveRecord
|
|
3
|
+
module HasManyAssociationWithScopes
|
|
4
|
+
def method_missing(method, *args, &block)
|
|
5
|
+
if responds_to_scope(method)
|
|
6
|
+
@reflection.klass.
|
|
7
|
+
search(:with => default_filter).
|
|
8
|
+
send(method, *args, &block)
|
|
9
|
+
else
|
|
10
|
+
super
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
def responds_to_scope(scope)
|
|
16
|
+
@reflection.klass.respond_to?(:sphinx_scopes) &&
|
|
17
|
+
@reflection.klass.sphinx_scopes.include?(scope)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
require 'active_support/log_subscriber'
|
|
2
|
+
|
|
3
|
+
module ThinkingSphinx
|
|
4
|
+
module ActiveRecord
|
|
5
|
+
class LogSubscriber < ActiveSupport::LogSubscriber
|
|
6
|
+
def self.runtime=(value)
|
|
7
|
+
Thread.current['thinking_sphinx_query_runtime'] = value
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def self.runtime
|
|
11
|
+
Thread.current['thinking_sphinx_query_runtime'] ||= 0
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.reset_runtime
|
|
15
|
+
rt, self.runtime = runtime, 0
|
|
16
|
+
rt
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def initialize
|
|
20
|
+
super
|
|
21
|
+
@odd_or_even = false
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def query(event)
|
|
25
|
+
self.class.runtime += event.duration
|
|
26
|
+
return unless logger.debug?
|
|
27
|
+
|
|
28
|
+
identifier = color('Sphinx Query (%.1fms)' % event.duration, GREEN, true)
|
|
29
|
+
query = event.payload[:query]
|
|
30
|
+
query = color query, nil, true if odd?
|
|
31
|
+
|
|
32
|
+
debug " #{identifier} #{query}"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def message(event)
|
|
36
|
+
return unless logger.debug?
|
|
37
|
+
|
|
38
|
+
identifier = color 'Sphinx', GREEN, true
|
|
39
|
+
message = event.payload[:message]
|
|
40
|
+
message = color message, nil, true if odd?
|
|
41
|
+
|
|
42
|
+
debug " #{identifier} #{message}"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def odd?
|
|
46
|
+
@odd_or_even = !@odd_or_even
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def logger
|
|
50
|
+
return @logger if defined? @logger
|
|
51
|
+
self.logger = ::ActiveRecord::Base.logger
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def logger=(logger)
|
|
55
|
+
@logger = logger
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
attach_to :thinking_sphinx
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
module ThinkingSphinx
|
|
2
|
+
module ActiveRecord
|
|
3
|
+
module Scopes
|
|
4
|
+
def self.included(base)
|
|
5
|
+
base.class_eval do
|
|
6
|
+
extend ThinkingSphinx::ActiveRecord::Scopes::ClassMethods
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
module ClassMethods
|
|
11
|
+
|
|
12
|
+
# Similar to ActiveRecord's default_scope method Thinking Sphinx supports
|
|
13
|
+
# a default_sphinx_scope. For example:
|
|
14
|
+
#
|
|
15
|
+
# default_sphinx_scope :some_sphinx_named_scope
|
|
16
|
+
#
|
|
17
|
+
# The scope is automatically applied when the search method is called. It
|
|
18
|
+
# will only be applied if it is an existing sphinx_scope.
|
|
19
|
+
def default_sphinx_scope(sphinx_scope_name)
|
|
20
|
+
add_sphinx_scopes_support_to_has_many_associations
|
|
21
|
+
@default_sphinx_scope = sphinx_scope_name
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Returns the default_sphinx_scope or nil if none is set.
|
|
25
|
+
def get_default_sphinx_scope
|
|
26
|
+
@default_sphinx_scope
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Returns true if the current Model has a default_sphinx_scope. Also checks if
|
|
30
|
+
# the default_sphinx_scope actually is a scope.
|
|
31
|
+
def has_default_sphinx_scope?
|
|
32
|
+
!@default_sphinx_scope.nil? && sphinx_scopes.include?(@default_sphinx_scope)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Similar to ActiveRecord's named_scope method Thinking Sphinx supports
|
|
36
|
+
# scopes. For example:
|
|
37
|
+
#
|
|
38
|
+
# sphinx_scope(:latest_first) {
|
|
39
|
+
# {:order => 'created_at DESC, @relevance DESC'}
|
|
40
|
+
# }
|
|
41
|
+
#
|
|
42
|
+
# Usage:
|
|
43
|
+
#
|
|
44
|
+
# @articles = Article.latest_first.search 'pancakes'
|
|
45
|
+
#
|
|
46
|
+
def sphinx_scope(method, &block)
|
|
47
|
+
add_sphinx_scopes_support_to_has_many_associations
|
|
48
|
+
|
|
49
|
+
@sphinx_scopes ||= []
|
|
50
|
+
@sphinx_scopes << method
|
|
51
|
+
|
|
52
|
+
singleton_class.instance_eval do
|
|
53
|
+
define_method(method) do |*args|
|
|
54
|
+
options = {:classes => classes_option}
|
|
55
|
+
options.merge! block.call(*args)
|
|
56
|
+
|
|
57
|
+
ThinkingSphinx::Search.new(options)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
define_method("#{method}_without_default".to_sym) do |*args|
|
|
61
|
+
options = {:classes => classes_option, :ignore_default => true}
|
|
62
|
+
options.merge! block.call(*args)
|
|
63
|
+
|
|
64
|
+
ThinkingSphinx::Search.new(options)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# This returns an Array of all defined scopes. The default
|
|
70
|
+
# scope shows as :default.
|
|
71
|
+
def sphinx_scopes
|
|
72
|
+
@sphinx_scopes || []
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def remove_sphinx_scopes
|
|
76
|
+
sphinx_scopes.each do |scope|
|
|
77
|
+
singleton_class.send(:undef_method, scope)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
sphinx_scopes.clear
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def add_sphinx_scopes_support_to_has_many_associations
|
|
84
|
+
mixin = sphinx_scopes_support_mixin
|
|
85
|
+
sphinx_scopes_support_classes.each do |klass|
|
|
86
|
+
klass.send(:include, mixin) unless klass.ancestors.include?(mixin)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def sphinx_scopes_support_classes
|
|
91
|
+
if ThinkingSphinx.rails_3_1?
|
|
92
|
+
[::ActiveRecord::Associations::CollectionProxy]
|
|
93
|
+
else
|
|
94
|
+
[::ActiveRecord::Associations::HasManyAssociation,
|
|
95
|
+
::ActiveRecord::Associations::HasManyThroughAssociation]
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def sphinx_scopes_support_mixin
|
|
100
|
+
if ThinkingSphinx.rails_3_1?
|
|
101
|
+
::ThinkingSphinx::ActiveRecord::CollectionProxyWithScopes
|
|
102
|
+
else
|
|
103
|
+
::ThinkingSphinx::ActiveRecord::HasManyAssociationWithScopes
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|