thinking-sphinx 1.2.12
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/LICENCE +20 -0
- data/README.textile +157 -0
- data/VERSION.yml +4 -0
- data/lib/thinking_sphinx.rb +211 -0
- data/lib/thinking_sphinx/active_record.rb +307 -0
- data/lib/thinking_sphinx/active_record/attribute_updates.rb +48 -0
- data/lib/thinking_sphinx/active_record/delta.rb +87 -0
- data/lib/thinking_sphinx/active_record/has_many_association.rb +28 -0
- data/lib/thinking_sphinx/active_record/scopes.rb +39 -0
- data/lib/thinking_sphinx/adapters/abstract_adapter.rb +42 -0
- data/lib/thinking_sphinx/adapters/mysql_adapter.rb +54 -0
- data/lib/thinking_sphinx/adapters/postgresql_adapter.rb +136 -0
- data/lib/thinking_sphinx/association.rb +164 -0
- data/lib/thinking_sphinx/attribute.rb +342 -0
- data/lib/thinking_sphinx/class_facet.rb +15 -0
- data/lib/thinking_sphinx/configuration.rb +282 -0
- data/lib/thinking_sphinx/core/array.rb +7 -0
- data/lib/thinking_sphinx/core/string.rb +15 -0
- data/lib/thinking_sphinx/deltas.rb +30 -0
- data/lib/thinking_sphinx/deltas/datetime_delta.rb +50 -0
- data/lib/thinking_sphinx/deltas/default_delta.rb +68 -0
- data/lib/thinking_sphinx/deltas/delayed_delta.rb +30 -0
- data/lib/thinking_sphinx/deltas/delayed_delta/delta_job.rb +24 -0
- data/lib/thinking_sphinx/deltas/delayed_delta/flag_as_deleted_job.rb +27 -0
- data/lib/thinking_sphinx/deltas/delayed_delta/job.rb +26 -0
- data/lib/thinking_sphinx/deploy/capistrano.rb +100 -0
- data/lib/thinking_sphinx/excerpter.rb +22 -0
- data/lib/thinking_sphinx/facet.rb +125 -0
- data/lib/thinking_sphinx/facet_search.rb +134 -0
- data/lib/thinking_sphinx/field.rb +82 -0
- data/lib/thinking_sphinx/index.rb +99 -0
- data/lib/thinking_sphinx/index/builder.rb +286 -0
- data/lib/thinking_sphinx/index/faux_column.rb +110 -0
- data/lib/thinking_sphinx/property.rb +162 -0
- data/lib/thinking_sphinx/rails_additions.rb +150 -0
- data/lib/thinking_sphinx/search.rb +707 -0
- data/lib/thinking_sphinx/search_methods.rb +421 -0
- data/lib/thinking_sphinx/source.rb +150 -0
- data/lib/thinking_sphinx/source/internal_properties.rb +46 -0
- data/lib/thinking_sphinx/source/sql.rb +128 -0
- data/lib/thinking_sphinx/tasks.rb +165 -0
- data/rails/init.rb +14 -0
- data/spec/lib/thinking_sphinx/active_record/delta_spec.rb +130 -0
- data/spec/lib/thinking_sphinx/active_record/has_many_association_spec.rb +49 -0
- data/spec/lib/thinking_sphinx/active_record/scopes_spec.rb +96 -0
- data/spec/lib/thinking_sphinx/active_record_spec.rb +364 -0
- data/spec/lib/thinking_sphinx/association_spec.rb +239 -0
- data/spec/lib/thinking_sphinx/attribute_spec.rb +500 -0
- data/spec/lib/thinking_sphinx/configuration_spec.rb +268 -0
- data/spec/lib/thinking_sphinx/core/array_spec.rb +9 -0
- data/spec/lib/thinking_sphinx/core/string_spec.rb +9 -0
- data/spec/lib/thinking_sphinx/excerpter_spec.rb +49 -0
- data/spec/lib/thinking_sphinx/facet_search_spec.rb +176 -0
- data/spec/lib/thinking_sphinx/facet_spec.rb +333 -0
- data/spec/lib/thinking_sphinx/field_spec.rb +154 -0
- data/spec/lib/thinking_sphinx/index/builder_spec.rb +455 -0
- data/spec/lib/thinking_sphinx/index/faux_column_spec.rb +30 -0
- data/spec/lib/thinking_sphinx/index_spec.rb +45 -0
- data/spec/lib/thinking_sphinx/rails_additions_spec.rb +203 -0
- data/spec/lib/thinking_sphinx/search_methods_spec.rb +152 -0
- data/spec/lib/thinking_sphinx/search_spec.rb +1092 -0
- data/spec/lib/thinking_sphinx/source_spec.rb +227 -0
- data/spec/lib/thinking_sphinx_spec.rb +162 -0
- data/tasks/distribution.rb +50 -0
- data/tasks/rails.rake +1 -0
- data/tasks/testing.rb +83 -0
- data/vendor/after_commit/LICENSE +20 -0
- data/vendor/after_commit/README +16 -0
- data/vendor/after_commit/Rakefile +22 -0
- data/vendor/after_commit/init.rb +8 -0
- data/vendor/after_commit/lib/after_commit.rb +45 -0
- data/vendor/after_commit/lib/after_commit/active_record.rb +114 -0
- data/vendor/after_commit/lib/after_commit/connection_adapters.rb +103 -0
- data/vendor/after_commit/test/after_commit_test.rb +53 -0
- data/vendor/delayed_job/lib/delayed/job.rb +251 -0
- data/vendor/delayed_job/lib/delayed/message_sending.rb +7 -0
- data/vendor/delayed_job/lib/delayed/performable_method.rb +55 -0
- data/vendor/delayed_job/lib/delayed/worker.rb +54 -0
- data/vendor/riddle/lib/riddle.rb +30 -0
- data/vendor/riddle/lib/riddle/client.rb +635 -0
- data/vendor/riddle/lib/riddle/client/filter.rb +53 -0
- data/vendor/riddle/lib/riddle/client/message.rb +66 -0
- data/vendor/riddle/lib/riddle/client/response.rb +84 -0
- data/vendor/riddle/lib/riddle/configuration.rb +33 -0
- data/vendor/riddle/lib/riddle/configuration/distributed_index.rb +48 -0
- data/vendor/riddle/lib/riddle/configuration/index.rb +142 -0
- data/vendor/riddle/lib/riddle/configuration/indexer.rb +19 -0
- data/vendor/riddle/lib/riddle/configuration/remote_index.rb +17 -0
- data/vendor/riddle/lib/riddle/configuration/searchd.rb +25 -0
- data/vendor/riddle/lib/riddle/configuration/section.rb +43 -0
- data/vendor/riddle/lib/riddle/configuration/source.rb +23 -0
- data/vendor/riddle/lib/riddle/configuration/sql_source.rb +34 -0
- data/vendor/riddle/lib/riddle/configuration/xml_source.rb +28 -0
- data/vendor/riddle/lib/riddle/controller.rb +53 -0
- metadata +172 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
require 'thinking_sphinx/deltas/default_delta'
|
|
2
|
+
require 'thinking_sphinx/deltas/delayed_delta'
|
|
3
|
+
require 'thinking_sphinx/deltas/datetime_delta'
|
|
4
|
+
|
|
5
|
+
module ThinkingSphinx
|
|
6
|
+
module Deltas
|
|
7
|
+
def self.parse(index)
|
|
8
|
+
delta_option = index.local_options.delete(:delta)
|
|
9
|
+
case delta_option
|
|
10
|
+
when TrueClass, :default
|
|
11
|
+
DefaultDelta.new index, index.local_options
|
|
12
|
+
when :delayed
|
|
13
|
+
DelayedDelta.new index, index.local_options
|
|
14
|
+
when :datetime
|
|
15
|
+
DatetimeDelta.new index, index.local_options
|
|
16
|
+
when FalseClass, nil
|
|
17
|
+
nil
|
|
18
|
+
else
|
|
19
|
+
if delta_option.is_a?(String)
|
|
20
|
+
delta_option = Kernel.const_get(delta_option)
|
|
21
|
+
end
|
|
22
|
+
if delta_option.ancestors.include?(ThinkingSphinx::Deltas::DefaultDelta)
|
|
23
|
+
delta_option.new index, index.local_options
|
|
24
|
+
else
|
|
25
|
+
raise "Unknown delta type"
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
module ThinkingSphinx
|
|
2
|
+
module Deltas
|
|
3
|
+
class DatetimeDelta < ThinkingSphinx::Deltas::DefaultDelta
|
|
4
|
+
attr_accessor :column, :threshold
|
|
5
|
+
|
|
6
|
+
def initialize(index, options)
|
|
7
|
+
@index = index
|
|
8
|
+
@column = options.delete(:delta_column) || :updated_at
|
|
9
|
+
@threshold = options.delete(:threshold) || 1.day
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def index(model, instance = nil)
|
|
13
|
+
# do nothing
|
|
14
|
+
true
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def delayed_index(model)
|
|
18
|
+
config = ThinkingSphinx::Configuration.instance
|
|
19
|
+
rotate = ThinkingSphinx.sphinx_running? ? "--rotate" : ""
|
|
20
|
+
|
|
21
|
+
output = `#{config.bin_path}#{config.indexer_binary_name} --config #{config.config_file} #{rotate} #{delta_index_name model}`
|
|
22
|
+
output += `#{config.bin_path}#{config.indexer_binary_name} --config #{config.config_file} #{rotate} --merge #{core_index_name model} #{delta_index_name model} --merge-dst-range sphinx_deleted 0 0`
|
|
23
|
+
puts output unless ThinkingSphinx.suppress_delta_output?
|
|
24
|
+
|
|
25
|
+
true
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def toggle(instance)
|
|
29
|
+
# do nothing
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def toggled(instance)
|
|
33
|
+
instance.send(@column) > @threshold.ago
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def reset_query(model)
|
|
37
|
+
nil
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def clause(model, toggled)
|
|
41
|
+
if toggled
|
|
42
|
+
"#{model.quoted_table_name}.#{model.connection.quote_column_name(@column.to_s)}" +
|
|
43
|
+
" > #{adapter.time_difference(@threshold)}"
|
|
44
|
+
else
|
|
45
|
+
nil
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
module ThinkingSphinx
|
|
2
|
+
module Deltas
|
|
3
|
+
class DefaultDelta
|
|
4
|
+
attr_accessor :column
|
|
5
|
+
|
|
6
|
+
def initialize(index, options)
|
|
7
|
+
@index = index
|
|
8
|
+
@column = options.delete(:delta_column) || :delta
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def index(model, instance = nil)
|
|
12
|
+
return true unless ThinkingSphinx.updates_enabled? &&
|
|
13
|
+
ThinkingSphinx.deltas_enabled?
|
|
14
|
+
return true if instance && !toggled(instance)
|
|
15
|
+
|
|
16
|
+
config = ThinkingSphinx::Configuration.instance
|
|
17
|
+
client = Riddle::Client.new config.address, config.port
|
|
18
|
+
rotate = ThinkingSphinx.sphinx_running? ? "--rotate" : ""
|
|
19
|
+
|
|
20
|
+
output = `#{config.bin_path}#{config.indexer_binary_name} --config #{config.config_file} #{rotate} #{delta_index_name model}`
|
|
21
|
+
puts(output) unless ThinkingSphinx.suppress_delta_output?
|
|
22
|
+
|
|
23
|
+
client.update(
|
|
24
|
+
core_index_name(model),
|
|
25
|
+
['sphinx_deleted'],
|
|
26
|
+
{instance.sphinx_document_id => [1]}
|
|
27
|
+
) if instance && ThinkingSphinx.sphinx_running? && instance.in_both_indexes?
|
|
28
|
+
|
|
29
|
+
true
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def toggle(instance)
|
|
33
|
+
instance.delta = true
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def toggled(instance)
|
|
37
|
+
instance.delta
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def reset_query(model)
|
|
41
|
+
"UPDATE #{model.quoted_table_name} SET " +
|
|
42
|
+
"#{model.connection.quote_column_name(@column.to_s)} = #{adapter.boolean(false)} " +
|
|
43
|
+
"WHERE #{model.connection.quote_column_name(@column.to_s)} = #{adapter.boolean(true)}"
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def clause(model, toggled)
|
|
47
|
+
"#{model.quoted_table_name}.#{model.connection.quote_column_name(@column.to_s)}" +
|
|
48
|
+
" = #{adapter.boolean(toggled)}"
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
protected
|
|
52
|
+
|
|
53
|
+
def core_index_name(model)
|
|
54
|
+
"#{model.source_of_sphinx_index.name.underscore.tr(':/\\', '_')}_core"
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def delta_index_name(model)
|
|
58
|
+
"#{model.source_of_sphinx_index.name.underscore.tr(':/\\', '_')}_delta"
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
private
|
|
62
|
+
|
|
63
|
+
def adapter
|
|
64
|
+
@adapter = @index.model.sphinx_database_adapter
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
require 'delayed/job'
|
|
2
|
+
|
|
3
|
+
require 'thinking_sphinx/deltas/delayed_delta/delta_job'
|
|
4
|
+
require 'thinking_sphinx/deltas/delayed_delta/flag_as_deleted_job'
|
|
5
|
+
require 'thinking_sphinx/deltas/delayed_delta/job'
|
|
6
|
+
|
|
7
|
+
module ThinkingSphinx
|
|
8
|
+
module Deltas
|
|
9
|
+
class DelayedDelta < ThinkingSphinx::Deltas::DefaultDelta
|
|
10
|
+
def index(model, instance = nil)
|
|
11
|
+
return true unless ThinkingSphinx.updates_enabled? && ThinkingSphinx.deltas_enabled?
|
|
12
|
+
return true if instance && !toggled(instance)
|
|
13
|
+
|
|
14
|
+
ThinkingSphinx::Deltas::Job.enqueue(
|
|
15
|
+
ThinkingSphinx::Deltas::DeltaJob.new(delta_index_name(model)),
|
|
16
|
+
ThinkingSphinx::Configuration.instance.delayed_job_priority
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
Delayed::Job.enqueue(
|
|
20
|
+
ThinkingSphinx::Deltas::FlagAsDeletedJob.new(
|
|
21
|
+
core_index_name(model), instance.sphinx_document_id
|
|
22
|
+
),
|
|
23
|
+
ThinkingSphinx::Configuration.instance.delayed_job_priority
|
|
24
|
+
) if instance
|
|
25
|
+
|
|
26
|
+
true
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module ThinkingSphinx
|
|
2
|
+
module Deltas
|
|
3
|
+
class DeltaJob
|
|
4
|
+
attr_accessor :index
|
|
5
|
+
|
|
6
|
+
def initialize(index)
|
|
7
|
+
@index = index
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def perform
|
|
11
|
+
return true unless ThinkingSphinx.updates_enabled? &&
|
|
12
|
+
ThinkingSphinx.deltas_enabled?
|
|
13
|
+
|
|
14
|
+
config = ThinkingSphinx::Configuration.instance
|
|
15
|
+
client = Riddle::Client.new config.address, config.port
|
|
16
|
+
|
|
17
|
+
output = `#{config.bin_path}#{config.indexer_binary_name} --config #{config.config_file} --rotate #{index}`
|
|
18
|
+
puts output unless ThinkingSphinx.suppress_delta_output?
|
|
19
|
+
|
|
20
|
+
true
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module ThinkingSphinx
|
|
2
|
+
module Deltas
|
|
3
|
+
class FlagAsDeletedJob
|
|
4
|
+
attr_accessor :index, :document_id
|
|
5
|
+
|
|
6
|
+
def initialize(index, document_id)
|
|
7
|
+
@index, @document_id = index, document_id
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def perform
|
|
11
|
+
return true unless ThinkingSphinx.updates_enabled?
|
|
12
|
+
|
|
13
|
+
config = ThinkingSphinx::Configuration.instance
|
|
14
|
+
client = Riddle::Client.new config.address, config.port
|
|
15
|
+
|
|
16
|
+
client.update(
|
|
17
|
+
@index,
|
|
18
|
+
['sphinx_deleted'],
|
|
19
|
+
{@document_id => [1]}
|
|
20
|
+
) if ThinkingSphinx.sphinx_running? &&
|
|
21
|
+
ThinkingSphinx::Search.search_for_id(@document_id, @index)
|
|
22
|
+
|
|
23
|
+
true
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
module ThinkingSphinx
|
|
2
|
+
module Deltas
|
|
3
|
+
class Job < Delayed::Job
|
|
4
|
+
def self.enqueue(object, priority = 0)
|
|
5
|
+
super unless duplicates_exist(object)
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def self.cancel_thinking_sphinx_jobs
|
|
9
|
+
if connection.tables.include?("delayed_jobs")
|
|
10
|
+
delete_all("handler LIKE '--- !ruby/object:ThinkingSphinx::Deltas::%'")
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
|
|
16
|
+
def self.duplicates_exist(object)
|
|
17
|
+
count(
|
|
18
|
+
:conditions => {
|
|
19
|
+
:handler => object.to_yaml,
|
|
20
|
+
:locked_at => nil
|
|
21
|
+
}
|
|
22
|
+
) > 0
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
Capistrano::Configuration.instance(:must_exist).load do
|
|
2
|
+
namespace :thinking_sphinx do
|
|
3
|
+
namespace :install do
|
|
4
|
+
desc <<-DESC
|
|
5
|
+
Install Sphinx by source
|
|
6
|
+
|
|
7
|
+
If Postgres is available, Sphinx will use it.
|
|
8
|
+
|
|
9
|
+
If the variable :thinking_sphinx_configure_args is set, it will
|
|
10
|
+
be passed to the Sphinx configure script. You can use this to
|
|
11
|
+
install Sphinx in a non-standard location:
|
|
12
|
+
|
|
13
|
+
set :thinking_sphinx_configure_args, "--prefix=$HOME/software"
|
|
14
|
+
DESC
|
|
15
|
+
|
|
16
|
+
task :sphinx do
|
|
17
|
+
with_postgres = false
|
|
18
|
+
begin
|
|
19
|
+
run "which pg_config" do |channel, stream, data|
|
|
20
|
+
with_postgres = !(data.nil? || data == "")
|
|
21
|
+
end
|
|
22
|
+
rescue Capistrano::CommandError => e
|
|
23
|
+
puts "Continuing despite error: #{e.message}"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
args = []
|
|
27
|
+
if with_postgres
|
|
28
|
+
run "pg_config --pkgincludedir" do |channel, stream, data|
|
|
29
|
+
args << "--with-pgsql=#{data}"
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
args << fetch(:thinking_sphinx_configure_args, '')
|
|
33
|
+
|
|
34
|
+
commands = <<-CMD
|
|
35
|
+
wget -q http://www.sphinxsearch.com/downloads/sphinx-0.9.8.1.tar.gz >> sphinx.log
|
|
36
|
+
tar xzvf sphinx-0.9.8.1.tar.gz
|
|
37
|
+
cd sphinx-0.9.8.1
|
|
38
|
+
./configure #{args.join(" ")}
|
|
39
|
+
make
|
|
40
|
+
#{try_sudo} make install
|
|
41
|
+
rm -rf sphinx-0.9.8.1 sphinx-0.9.8.1.tar.gz
|
|
42
|
+
CMD
|
|
43
|
+
run commands.split(/\n\s+/).join(" && ")
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
desc "Install Thinking Sphinx as a gem from GitHub"
|
|
47
|
+
task :ts do
|
|
48
|
+
run "#{try_sudo} gem install thinking-sphinx --source http://gemcutter.org"
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
desc "Generate the Sphinx configuration file"
|
|
53
|
+
task :configure do
|
|
54
|
+
rake "thinking_sphinx:configure"
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
desc "Index data"
|
|
58
|
+
task :index do
|
|
59
|
+
rake "thinking_sphinx:index"
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
desc "Start the Sphinx daemon"
|
|
63
|
+
task :start do
|
|
64
|
+
configure
|
|
65
|
+
rake "thinking_sphinx:start"
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
desc "Stop the Sphinx daemon"
|
|
69
|
+
task :stop do
|
|
70
|
+
configure
|
|
71
|
+
rake "thinking_sphinx:stop"
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
desc "Stop and then start the Sphinx daemon"
|
|
75
|
+
task :restart do
|
|
76
|
+
stop
|
|
77
|
+
start
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
desc "Stop, re-index and then start the Sphinx daemon"
|
|
81
|
+
task :rebuild do
|
|
82
|
+
stop
|
|
83
|
+
index
|
|
84
|
+
start
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
desc "Add the shared folder for sphinx files for the production environment"
|
|
88
|
+
task :shared_sphinx_folder, :roles => :web do
|
|
89
|
+
run "mkdir -p #{shared_path}/db/sphinx/production"
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def rake(*tasks)
|
|
93
|
+
rails_env = fetch(:rails_env, "production")
|
|
94
|
+
rake = fetch(:rake, "rake")
|
|
95
|
+
tasks.each do |t|
|
|
96
|
+
run "cd #{current_path}; #{rake} RAILS_ENV=#{rails_env} #{t}"
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module ThinkingSphinx
|
|
2
|
+
class Excerpter
|
|
3
|
+
CoreMethods = %w( kind_of? object_id respond_to? should should_not stub! )
|
|
4
|
+
# Hide most methods, to allow them to be passed through to the instance.
|
|
5
|
+
instance_methods.select { |method|
|
|
6
|
+
method.to_s[/^__/].nil? && !CoreMethods.include?(method.to_s)
|
|
7
|
+
}.each { |method|
|
|
8
|
+
undef_method method
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
def initialize(search, instance)
|
|
12
|
+
@search = search
|
|
13
|
+
@instance = instance
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def method_missing(method, *args, &block)
|
|
17
|
+
string = @instance.send(method, *args, &block).to_s
|
|
18
|
+
|
|
19
|
+
@search.excerpt_for(string, @instance.class)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
module ThinkingSphinx
|
|
2
|
+
class Facet
|
|
3
|
+
attr_reader :property
|
|
4
|
+
|
|
5
|
+
def initialize(property)
|
|
6
|
+
@property = property
|
|
7
|
+
|
|
8
|
+
if property.columns.length != 1
|
|
9
|
+
raise "Can't translate Facets on multiple-column field or attribute"
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.name_for(facet)
|
|
14
|
+
case facet
|
|
15
|
+
when Facet
|
|
16
|
+
facet.name
|
|
17
|
+
when String, Symbol
|
|
18
|
+
facet.to_s.gsub(/(_facet|_crc)$/,'').to_sym
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def self.attribute_name_for(name)
|
|
23
|
+
name.to_s == 'class' ? 'class_crc' : "#{name}_facet"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def self.attribute_name_from_value(name, value)
|
|
27
|
+
case value
|
|
28
|
+
when String
|
|
29
|
+
attribute_name_for(name)
|
|
30
|
+
when Array
|
|
31
|
+
if value.all? { |val| val.is_a?(Integer) }
|
|
32
|
+
name
|
|
33
|
+
else
|
|
34
|
+
attribute_name_for(name)
|
|
35
|
+
end
|
|
36
|
+
else
|
|
37
|
+
name
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def self.translate?(property)
|
|
42
|
+
return true if property.is_a?(Field)
|
|
43
|
+
|
|
44
|
+
case property.type
|
|
45
|
+
when :string
|
|
46
|
+
true
|
|
47
|
+
when :integer, :boolean, :datetime, :float
|
|
48
|
+
false
|
|
49
|
+
when :multi
|
|
50
|
+
!property.all_ints?
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def name
|
|
55
|
+
property.unique_name
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def attribute_name
|
|
59
|
+
if translate?
|
|
60
|
+
Facet.attribute_name_for(@property.unique_name)
|
|
61
|
+
else
|
|
62
|
+
@property.unique_name.to_s
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def translate?
|
|
67
|
+
Facet.translate?(@property)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def type
|
|
71
|
+
@property.is_a?(Field) ? :string : @property.type
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def value(object, attribute_value)
|
|
75
|
+
return translate(object, attribute_value) if translate? || float?
|
|
76
|
+
|
|
77
|
+
case @property.type
|
|
78
|
+
when :datetime
|
|
79
|
+
Time.at(attribute_value)
|
|
80
|
+
when :boolean
|
|
81
|
+
attribute_value > 0
|
|
82
|
+
else
|
|
83
|
+
attribute_value
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def to_s
|
|
88
|
+
name
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
private
|
|
92
|
+
|
|
93
|
+
def translate(object, attribute_value)
|
|
94
|
+
objects = source_objects(object)
|
|
95
|
+
return nil if objects.nil? || objects.empty?
|
|
96
|
+
|
|
97
|
+
if objects.length > 1
|
|
98
|
+
objects.collect { |item| item.send(column.__name) }.detect { |item|
|
|
99
|
+
item.to_crc32 == attribute_value
|
|
100
|
+
}
|
|
101
|
+
else
|
|
102
|
+
objects.first.send(column.__name)
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def source_objects(object)
|
|
107
|
+
column.__stack.each { |method|
|
|
108
|
+
object = Array(object).collect { |item|
|
|
109
|
+
item.send(method)
|
|
110
|
+
}.flatten.compact
|
|
111
|
+
|
|
112
|
+
return nil if object.empty?
|
|
113
|
+
}
|
|
114
|
+
Array(object)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def column
|
|
118
|
+
@property.columns.first
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def float?
|
|
122
|
+
@property.type == :float
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|