freelancing-god-thinking-sphinx 1.1.3 → 1.1.4

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 CHANGED
@@ -48,7 +48,7 @@ database, causing the next run to have failures. Let that run complete and then
48
48
  Since I first released this library, there's been quite a few people who have submitted patches, to my immense gratitude. Others have suggested syntax changes and general improvements. So my thanks to the following people:
49
49
 
50
50
  - Joost Hietbrink
51
- - Jonathon Conway
51
+ - Jonathan Conway
52
52
  - Gregory Mirzayantz
53
53
  - Tung Nguyen
54
54
  - Sean Cribbs
@@ -103,3 +103,5 @@ Since I first released this library, there's been quite a few people who have su
103
103
  - Lachie Cox
104
104
  - Lourens Naude
105
105
  - Tom Davies
106
+ - Dan Pickett
107
+ - Alex Caudill
@@ -13,6 +13,7 @@ require 'thinking_sphinx/attribute'
13
13
  require 'thinking_sphinx/collection'
14
14
  require 'thinking_sphinx/configuration'
15
15
  require 'thinking_sphinx/facet'
16
+ require 'thinking_sphinx/facet_collection'
16
17
  require 'thinking_sphinx/field'
17
18
  require 'thinking_sphinx/index'
18
19
  require 'thinking_sphinx/rails_additions'
@@ -33,7 +34,7 @@ module ThinkingSphinx
33
34
  module Version #:nodoc:
34
35
  Major = 1
35
36
  Minor = 1
36
- Tiny = 3
37
+ Tiny = 4
37
38
 
38
39
  String = [Major, Minor, Tiny].join('.')
39
40
  end
@@ -11,7 +11,8 @@ module ThinkingSphinx
11
11
 
12
12
  def self.detect(model)
13
13
  case model.connection.class.name
14
- when "ActiveRecord::ConnectionAdapters::MysqlAdapter"
14
+ when "ActiveRecord::ConnectionAdapters::MysqlAdapter",
15
+ "ActiveRecord::ConnectionAdapters::MysqlplusAdapter"
15
16
  ThinkingSphinx::MysqlAdapter.new model
16
17
  when "ActiveRecord::ConnectionAdapters::PostgreSQLAdapter"
17
18
  ThinkingSphinx::PostgreSQLAdapter.new model
@@ -30,4 +31,4 @@ module ThinkingSphinx
30
31
  @connection ||= @model.connection
31
32
  end
32
33
  end
33
- end
34
+ end
@@ -11,7 +11,7 @@ module ThinkingSphinx
11
11
 
12
12
  def concatenate(clause, separator = ' ')
13
13
  clause.split(', ').collect { |field|
14
- "COALESCE(#{field}, '')"
14
+ "COALESCE(CAST(#{field} as varchar), '')"
15
15
  }.join(" || '#{separator}' || ")
16
16
  end
17
17
 
@@ -140,7 +140,7 @@ module ThinkingSphinx
140
140
  # actual column's datatype is and returns that.
141
141
  def type
142
142
  @type ||= case
143
- when is_many?
143
+ when is_many?, is_many_ints?
144
144
  :multi
145
145
  when @associations.values.flatten.length > 1
146
146
  :string
@@ -152,7 +152,7 @@ module ThinkingSphinx
152
152
  def to_facet
153
153
  return nil unless @faceted
154
154
 
155
- ThinkingSphinx::Facet.new(unique_name, @columns, self)
155
+ ThinkingSphinx::Facet.new(self)
156
156
  end
157
157
 
158
158
  private
@@ -206,6 +206,10 @@ module ThinkingSphinx
206
206
  associations.values.flatten.any? { |assoc| assoc.is_many? }
207
207
  end
208
208
 
209
+ def is_many_ints?
210
+ concat_ws? && all_ints?
211
+ end
212
+
209
213
  # Returns true if any of the columns are string values, instead of database
210
214
  # column references.
211
215
  def is_string?
@@ -130,5 +130,13 @@ module ThinkingSphinx
130
130
  yield self[index], match[:weight]
131
131
  end
132
132
  end
133
+
134
+ def inject_with_groupby_and_count(initial = nil, &block)
135
+ index = -1
136
+ results[:matches].inject(initial) do |memo, match|
137
+ index += 1
138
+ yield memo, self[index], match[:attributes]["@groupby"], match[:attributes]["@count"]
139
+ end
140
+ end
133
141
  end
134
- end
142
+ end
@@ -0,0 +1,58 @@
1
+ module ThinkingSphinx
2
+ class Facet
3
+ attr_reader :reference
4
+
5
+ def initialize(reference)
6
+ @reference = reference
7
+
8
+ if reference.columns.length != 1
9
+ raise "Can't translate Facets on multiple-column field or attribute"
10
+ end
11
+ end
12
+
13
+ def name
14
+ reference.unique_name
15
+ end
16
+
17
+ def attribute_name
18
+ @attribute_name ||= case @reference
19
+ when Attribute
20
+ @reference.unique_name.to_s
21
+ when Field
22
+ @reference.unique_name.to_s + "_sort"
23
+ end
24
+ end
25
+
26
+ def value(object, attribute_value)
27
+ return translate(object, attribute_value) if @reference.is_a?(Field)
28
+
29
+ case @reference.type
30
+ when :string, :multi
31
+ translate(object, attribute_value)
32
+ when :datetime
33
+ Time.at(attribute_value)
34
+ when :boolean
35
+ attribute_value > 0
36
+ else
37
+ attribute_value
38
+ end
39
+ end
40
+
41
+ def to_s
42
+ name
43
+ end
44
+
45
+ private
46
+
47
+ def translate(object, attribute_value)
48
+ column.__stack.each { |method|
49
+ object = object.send(method)
50
+ }
51
+ object.send(column.__name)
52
+ end
53
+
54
+ def column
55
+ @reference.columns.first
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,44 @@
1
+ module ThinkingSphinx
2
+ class FacetCollection < Hash
3
+ attr_accessor :arguments
4
+
5
+ def initialize(arguments)
6
+ @arguments = arguments.clone
7
+ @attribute_values = {}
8
+ @facets = []
9
+ end
10
+
11
+ def add_from_results(facet, results)
12
+ self[facet.name] = {}
13
+ @attribute_values[facet.name] = {}
14
+ @facets << facet
15
+
16
+ results.each_with_groupby_and_count { |result, group, count|
17
+ facet_value = facet.value(result, group)
18
+
19
+ self[facet.name][facet_value] = count
20
+ @attribute_values[facet.name][facet_value] = group
21
+ }
22
+ end
23
+
24
+ def for(hash = {})
25
+ arguments = @arguments.clone
26
+ options = arguments.extract_options!
27
+ options[:with] ||= {}
28
+
29
+ hash.each do |key, value|
30
+ attrib = facet_for_key(key).attribute_name
31
+ options[:with][attrib] = @attribute_values[key][value]
32
+ end
33
+
34
+ arguments << options
35
+ ThinkingSphinx::Search.search *arguments
36
+ end
37
+
38
+ private
39
+
40
+ def facet_for_key(key)
41
+ @facets.detect { |facet| facet.name == key }
42
+ end
43
+ end
44
+ end
@@ -115,7 +115,7 @@ module ThinkingSphinx
115
115
  def to_facet
116
116
  return nil unless @faceted
117
117
 
118
- ThinkingSphinx::Facet.new(unique_name, @columns, self)
118
+ ThinkingSphinx::Facet.new(self)
119
119
  end
120
120
 
121
121
  private
@@ -159,21 +159,22 @@ module ThinkingSphinx
159
159
  stored_class = @model.store_full_sti_class ? @model.name : @model.name.demodulize
160
160
  builder.where("#{@model.quoted_table_name}.#{quote_column(@model.inheritance_column)} = '#{stored_class}'")
161
161
  end
162
-
163
- @fields = builder.fields
164
- @attributes = builder.attributes
162
+
163
+ set_model = Proc.new { |item| item.model = @model }
164
+
165
+ @fields = builder.fields &set_model
166
+ @attributes = builder.attributes.each &set_model
165
167
  @conditions = builder.conditions
166
168
  @groupings = builder.groupings
167
169
  @delta_object = ThinkingSphinx::Deltas.parse self, builder.properties
168
170
  @options = builder.properties
169
171
 
172
+ is_faceted = Proc.new { |item| item.faceted }
173
+ add_facet = Proc.new { |item| @model.sphinx_facets << item.to_facet }
174
+
170
175
  @model.sphinx_facets ||= []
171
- @fields.select { |field| field.faceted }.each { |field|
172
- @model.sphinx_facets << field.to_facet
173
- }
174
- @attributes.select { |attrib| attrib.faceted }.each { |attrib|
175
- @model.sphinx_facets << attrib.to_facet
176
- }
176
+ @fields.select( &is_faceted).each &add_facet
177
+ @attributes.select(&is_faceted).each &add_facet
177
178
 
178
179
  # We want to make sure that if the database doesn't exist, then Thinking
179
180
  # Sphinx doesn't mind when running non-TS tasks (like db:create, db:drop
@@ -277,7 +278,7 @@ module ThinkingSphinx
277
278
  config = @model.connection.instance_variable_get(:@config)
278
279
 
279
280
  source.sql_host = config[:host] || "localhost"
280
- source.sql_user = config[:username] || config[:user]
281
+ source.sql_user = config[:username] || config[:user] || ""
281
282
  source.sql_pass = (config[:password].to_s || "").gsub('#', '\#')
282
283
  source.sql_db = config[:database]
283
284
  source.sql_port = config[:port]
@@ -147,6 +147,15 @@ module ThinkingSphinx
147
147
  end
148
148
  alias_method :attribute, :has
149
149
 
150
+ def facet(*args)
151
+ options = args.extract_options!
152
+ options[:facet] = true
153
+
154
+ args.each do |columns|
155
+ attributes << Attribute.new(FauxColumn.coerce(columns), options)
156
+ end
157
+ end
158
+
150
159
  # Use this method to add some manual SQL conditions for your index
151
160
  # request. You can pass in as many strings as you like, they'll get
152
161
  # joined together with ANDs later on.
@@ -21,7 +21,7 @@ module ThinkingSphinx
21
21
 
22
22
  options = args.extract_options!
23
23
  page = options[:page] ? options[:page].to_i : 1
24
-
24
+
25
25
  ThinkingSphinx::Collection.ids_from_results(results, page, client.limit, options)
26
26
  end
27
27
 
@@ -353,18 +353,13 @@ module ThinkingSphinx
353
353
  end
354
354
 
355
355
  def facets(*args)
356
- options = args.extract_options!.merge! :group_function => :attr
356
+ hash = ThinkingSphinx::FacetCollection.new args
357
+ options = args.extract_options!.clone.merge! :group_function => :attr
357
358
 
358
- options[:class].sphinx_facets.inject({}) do |hash, facet|
359
- facet_result = {}
359
+ options[:class].sphinx_facets.inject(hash) do |hash, facet|
360
360
  options[:group_by] = facet.attribute_name
361
361
 
362
- results = search *(args + [options])
363
- results.each_with_groupby_and_count do |result, group, count|
364
- facet_result[facet.value(result, group)] = count
365
- end
366
- hash[facet.name] = facet_result
367
-
362
+ hash.add_from_results facet, search(*(args + [options]))
368
363
  hash
369
364
  end
370
365
  end
@@ -474,6 +469,13 @@ module ThinkingSphinx
474
469
  Riddle::Client::Filter.new attr.to_s, filter_value(val), true
475
470
  } if options[:without]
476
471
 
472
+ # every-match attribute filters
473
+ client.filters += options[:with_all].collect { |attr,vals|
474
+ Array(vals).collect { |val|
475
+ Riddle::Client::Filter.new attr.to_s, filter_value(val)
476
+ }
477
+ }.flatten if options[:with_all]
478
+
477
479
  # exclusive attribute filter on primary key
478
480
  client.filters += Array(options[:without_ids]).collect { |id|
479
481
  Riddle::Client::Filter.new 'sphinx_internal_id', filter_value(id), true
@@ -0,0 +1,128 @@
1
+ require 'fileutils'
2
+
3
+ namespace :thinking_sphinx do
4
+ task :app_env do
5
+ Rake::Task[:environment].invoke if defined?(RAILS_ROOT)
6
+ Rake::Task[:merb_env].invoke if defined?(Merb)
7
+ end
8
+
9
+ desc "Stop if running, then start a Sphinx searchd daemon using Thinking Sphinx's settings"
10
+ task :running_start => :app_env do
11
+ Rake::Task["thinking_sphinx:stop"].invoke if sphinx_running?
12
+ Rake::Task["thinking_sphinx:start"].invoke
13
+ end
14
+
15
+ desc "Start a Sphinx searchd daemon using Thinking Sphinx's settings"
16
+ task :start => :app_env do
17
+ config = ThinkingSphinx::Configuration.instance
18
+
19
+ FileUtils.mkdir_p config.searchd_file_path
20
+ raise RuntimeError, "searchd is already running." if sphinx_running?
21
+
22
+ Dir["#{config.searchd_file_path}/*.spl"].each { |file| File.delete(file) }
23
+
24
+ cmd = "#{config.bin_path}searchd --pidfile --config #{config.config_file}"
25
+ puts cmd
26
+ system cmd
27
+
28
+ sleep(2)
29
+
30
+ if sphinx_running?
31
+ puts "Started successfully (pid #{sphinx_pid})."
32
+ else
33
+ puts "Failed to start searchd daemon. Check #{config.searchd_log_file}."
34
+ end
35
+ end
36
+
37
+ desc "Stop Sphinx using Thinking Sphinx's settings"
38
+ task :stop => :app_env do
39
+ raise RuntimeError, "searchd is not running." unless sphinx_running?
40
+ config = ThinkingSphinx::Configuration.instance
41
+ pid = sphinx_pid
42
+ system "searchd --stop --config #{config.config_file}"
43
+ puts "Stopped search daemon (pid #{pid})."
44
+ end
45
+
46
+ desc "Restart Sphinx"
47
+ task :restart => [:app_env, :stop, :start]
48
+
49
+ desc "Generate the Sphinx configuration file using Thinking Sphinx's settings"
50
+ task :configure => :app_env do
51
+ config = ThinkingSphinx::Configuration.instance
52
+ puts "Generating Configuration to #{config.config_file}"
53
+ config.build
54
+ end
55
+
56
+ desc "Index data for Sphinx using Thinking Sphinx's settings"
57
+ task :index => :app_env do
58
+ ThinkingSphinx::Deltas::Job.cancel_thinking_sphinx_jobs
59
+
60
+ config = ThinkingSphinx::Configuration.instance
61
+ unless ENV["INDEX_ONLY"] == "true"
62
+ puts "Generating Configuration to #{config.config_file}"
63
+ config.build
64
+ end
65
+
66
+ FileUtils.mkdir_p config.searchd_file_path
67
+ cmd = "#{config.bin_path}indexer --config #{config.config_file} --all"
68
+ cmd << " --rotate" if sphinx_running?
69
+ puts cmd
70
+ system cmd
71
+ end
72
+
73
+ namespace :index do
74
+ task :delta => :app_env do
75
+ ThinkingSphinx.indexed_models.select { |model|
76
+ model.constantize.sphinx_indexes.any? { |index| index.delta? }
77
+ }.each do |model|
78
+ model.constantize.sphinx_indexes.select { |index|
79
+ index.delta? && index.delta_object.respond_to?(:delayed_index)
80
+ }.each { |index|
81
+ index.delta_object.delayed_index(index.model)
82
+ }
83
+ end
84
+ end
85
+ end
86
+
87
+ desc "Process stored delta index requests"
88
+ task :delayed_delta => :app_env do
89
+ require 'delayed/worker'
90
+
91
+ Delayed::Worker.new(
92
+ :min_priority => ENV['MIN_PRIORITY'],
93
+ :max_priority => ENV['MAX_PRIORITY']
94
+ ).start
95
+ end
96
+ end
97
+
98
+ namespace :ts do
99
+ desc "Stop if running, then start a Sphinx searchd daemon using Thinking Sphinx's settings"
100
+ task :run => "thinking_sphinx:running_start"
101
+ desc "Start a Sphinx searchd daemon using Thinking Sphinx's settings"
102
+ task :start => "thinking_sphinx:start"
103
+ desc "Stop Sphinx using Thinking Sphinx's settings"
104
+ task :stop => "thinking_sphinx:stop"
105
+ desc "Index data for Sphinx using Thinking Sphinx's settings"
106
+ task :in => "thinking_sphinx:index"
107
+ namespace :in do
108
+ desc "Index Thinking Sphinx datetime delta indexes"
109
+ task :delta => "thinking_sphinx:index:delta"
110
+ end
111
+ task :index => "thinking_sphinx:index"
112
+ desc "Restart Sphinx"
113
+ task :restart => "thinking_sphinx:restart"
114
+ desc "Generate the Sphinx configuration file using Thinking Sphinx's settings"
115
+ task :conf => "thinking_sphinx:configure"
116
+ desc "Generate the Sphinx configuration file using Thinking Sphinx's settings"
117
+ task :config => "thinking_sphinx:configure"
118
+ desc "Process stored delta index requests"
119
+ task :dd => "thinking_sphinx:delayed_delta"
120
+ end
121
+
122
+ def sphinx_pid
123
+ ThinkingSphinx.sphinx_pid
124
+ end
125
+
126
+ def sphinx_running?
127
+ ThinkingSphinx.sphinx_running?
128
+ end
@@ -157,7 +157,7 @@ describe ThinkingSphinx::Attribute do
157
157
  end
158
158
 
159
159
  it "should return :string if there's more than one association" do
160
- @attribute.associations = {:a => :assoc, :b => :assoc}
160
+ @attribute.associations = {:a => [:assoc], :b => [:assoc]}
161
161
  @attribute.send(:type).should == :string
162
162
  end
163
163
 
data/tasks/rails.rake ADDED
@@ -0,0 +1 @@
1
+ require File.join(File.dirname(__FILE__), '/../lib/thinking_sphinx/tasks')
@@ -18,7 +18,7 @@ module Riddle #:nodoc:
18
18
  Rev = 1533
19
19
  # Release number to mark my own fixes, beyond feature parity with
20
20
  # Sphinx itself.
21
- Release = 3
21
+ Release = 4
22
22
 
23
23
  String = [Major, Minor, Tiny].join('.')
24
24
  GemVersion = [Major, Minor, Tiny, Rev, Release].join('.')
@@ -456,7 +456,7 @@ module Riddle
456
456
  header = socket.recv(8)
457
457
  status, version, length = header.unpack('n2N')
458
458
 
459
- while response.length < length
459
+ while response.length < (length || 0)
460
460
  part = socket.recv(length - response.length)
461
461
  response << part if part
462
462
  end
@@ -20,13 +20,18 @@ module Riddle
20
20
  if send(setting) == ""
21
21
  conf = " #{setting} = "
22
22
  else
23
- conf = Array(send(setting)).collect { |set|
23
+ conf = setting_to_array(setting).collect { |set|
24
24
  " #{setting} = #{set}"
25
25
  }
26
26
  end
27
27
  conf.length == 0 ? nil : conf
28
28
  }.flatten.compact
29
29
  end
30
+
31
+ def setting_to_array(setting)
32
+ value = send(setting)
33
+ value.is_a?(Array) ? value : [value]
34
+ end
30
35
  end
31
36
  end
32
- end
37
+ end
@@ -14,7 +14,7 @@ module Riddle
14
14
  def start
15
15
  return if running?
16
16
 
17
- cmd = "searchd --config #{@path}"
17
+ cmd = "searchd --pidfile --config #{@path}"
18
18
  `#{cmd}`
19
19
 
20
20
  sleep(1)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: freelancing-god-thinking-sphinx
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.3
4
+ version: 1.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pat Allan
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-01-17 00:00:00 -08:00
12
+ date: 2009-02-04 00:00:00 -08:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -41,19 +41,21 @@ files:
41
41
  - lib/thinking_sphinx/deltas/delayed_delta/job.rb
42
42
  - lib/thinking_sphinx/deltas/delayed_delta.rb
43
43
  - lib/thinking_sphinx/deltas.rb
44
+ - lib/thinking_sphinx/facet.rb
45
+ - lib/thinking_sphinx/facet_collection.rb
44
46
  - lib/thinking_sphinx/field.rb
45
47
  - lib/thinking_sphinx/index/builder.rb
46
48
  - lib/thinking_sphinx/index/faux_column.rb
47
49
  - lib/thinking_sphinx/index.rb
48
50
  - lib/thinking_sphinx/rails_additions.rb
49
51
  - lib/thinking_sphinx/search.rb
52
+ - lib/thinking_sphinx/tasks.rb
50
53
  - lib/thinking_sphinx.rb
51
54
  - LICENCE
52
55
  - README
53
56
  - tasks/distribution.rb
54
57
  - tasks/testing.rb
55
- - tasks/thinking_sphinx_tasks.rb
56
- - tasks/thinking_sphinx_tasks.rake
58
+ - tasks/rails.rake
57
59
  - vendor/after_commit
58
60
  - vendor/after_commit/init.rb
59
61
  - vendor/after_commit/lib