ultrasphinx 1.8 → 1.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. data.tar.gz.sig +1 -3
  2. data/CHANGELOG +4 -0
  3. data/DEPLOYMENT_NOTES +7 -3
  4. data/Manifest +10 -0
  5. data/RAKE_TASKS +4 -2
  6. data/README +35 -18
  7. data/TODO +0 -1
  8. data/examples/default.base +24 -19
  9. data/lib/ultrasphinx.rb +1 -1
  10. data/lib/ultrasphinx/configure.rb +66 -25
  11. data/lib/ultrasphinx/core_extensions.rb +10 -1
  12. data/lib/ultrasphinx/is_indexed.rb +49 -6
  13. data/lib/ultrasphinx/search.rb +81 -69
  14. data/lib/ultrasphinx/search/internals.rb +61 -38
  15. data/lib/ultrasphinx/search/parser.rb +17 -7
  16. data/lib/ultrasphinx/ultrasphinx.rb +69 -16
  17. data/tasks/ultrasphinx.rake +47 -29
  18. data/test/config/ultrasphinx/test.base +50 -21
  19. data/test/integration/app/app/models/geo/address.rb +2 -1
  20. data/test/integration/app/app/models/person/user.rb +12 -3
  21. data/test/integration/app/app/models/seller.rb +2 -1
  22. data/test/integration/app/config/environment.rb +2 -0
  23. data/test/integration/app/config/ultrasphinx/default.base +16 -8
  24. data/test/integration/app/config/ultrasphinx/development.conf +319 -0
  25. data/test/integration/app/config/ultrasphinx/development.conf.canonical +152 -24
  26. data/test/integration/app/db/schema.rb +56 -0
  27. data/test/integration/app/test/fixtures/sellers.yml +1 -1
  28. data/test/integration/app/test/fixtures/users.yml +1 -1
  29. data/test/integration/app/test/unit/country_test.rb +8 -0
  30. data/test/integration/delta_test.rb +39 -0
  31. data/test/integration/search_test.rb +60 -1
  32. data/test/integration/spell_test.rb +6 -2
  33. data/test/profile/benchmark.rb +44 -0
  34. data/test/test_helper.rb +6 -1
  35. data/test/unit/parser_test.rb +21 -2
  36. data/ultrasphinx.gemspec +4 -4
  37. data/vendor/riddle/README +2 -2
  38. data/vendor/riddle/lib/riddle.rb +1 -1
  39. data/vendor/riddle/lib/riddle/client.rb +45 -10
  40. data/vendor/riddle/spec/fixtures/data/anchor.bin +0 -0
  41. data/vendor/riddle/spec/fixtures/data/any.bin +0 -0
  42. data/vendor/riddle/spec/fixtures/data/boolean.bin +0 -0
  43. data/vendor/riddle/spec/fixtures/data/distinct.bin +0 -0
  44. data/vendor/riddle/spec/fixtures/data/field_weights.bin +0 -0
  45. data/vendor/riddle/spec/fixtures/data/filter.bin +0 -0
  46. data/vendor/riddle/spec/fixtures/data/group.bin +0 -0
  47. data/vendor/riddle/spec/fixtures/data/index.bin +0 -0
  48. data/vendor/riddle/spec/fixtures/data/index_weights.bin +0 -0
  49. data/vendor/riddle/spec/fixtures/data/phrase.bin +0 -0
  50. data/vendor/riddle/spec/fixtures/data/rank_mode.bin +0 -0
  51. data/vendor/riddle/spec/fixtures/data/simple.bin +0 -0
  52. data/vendor/riddle/spec/fixtures/data/sort.bin +0 -0
  53. data/vendor/riddle/spec/fixtures/data/update_simple.bin +0 -0
  54. data/vendor/riddle/spec/fixtures/data/weights.bin +0 -0
  55. data/vendor/riddle/spec/fixtures/data_generator.php +130 -0
  56. data/vendor/riddle/spec/fixtures/sphinxapi.php +1066 -0
  57. data/vendor/riddle/spec/spec_helper.rb +1 -0
  58. data/vendor/riddle/spec/unit/client_spec.rb +18 -4
  59. metadata +12 -2
  60. metadata.gz.sig +0 -0
@@ -2,8 +2,7 @@
2
2
  module Ultrasphinx
3
3
  class Search
4
4
  module Parser
5
- # We could rewrite this in Treetop, but since it works well, what's the point? It's
6
- # probably faster this way. Ragel would speed it up, but be too much work.
5
+ # We could rewrite this in Treetop, but for now it works well.
7
6
 
8
7
  class Error < RuntimeError
9
8
  end
@@ -50,7 +49,15 @@ module Ultrasphinx
50
49
  query << ")" if field and contents.size > 1
51
50
  end
52
51
 
53
- # XXX swap the first pair if the order is reversed
52
+ # Collapse fieldsets early so that the swap doesn't split them
53
+ query.each_with_index do |token, index|
54
+ if token =~ /^@/
55
+ query[index] = "#{token} #{query[index + 1]}"
56
+ query[index + 1] = nil
57
+ end
58
+ end
59
+
60
+ # Swap the first pair if the order is reversed
54
61
  if [OPERATORS['NOT'], OPERATORS['OR']].include? query.first.upcase
55
62
  query[0], query[1] = query[1], query[0]
56
63
  end
@@ -83,10 +90,11 @@ module Ultrasphinx
83
90
  if subtoken =~ /\"(.*)\"/
84
91
  subtoken.sub!($1, $1.gsub(/[()]/, ''))
85
92
  end
86
-
87
- # add to the stream, converting the operator
93
+
94
+ # Add to the stream, converting the operator
88
95
  if !has_operator
89
- if OPERATORS.to_a.flatten.include? subtoken and index != (query.size - 1) # Operators at the end of the string are not parsed
96
+ if OPERATORS.to_a.flatten.include? subtoken and index != (query.size - 1)
97
+ # Note that operators at the end of the string are not parsed
90
98
  token_stream << OPERATORS[subtoken] || subtoken
91
99
  has_operator = true # flip
92
100
  else
@@ -103,7 +111,9 @@ module Ultrasphinx
103
111
  end
104
112
  end
105
113
 
106
- raise Error, "#{token_stream.inspect} is not a valid token stream" unless token_stream.size % 2 == 0
114
+ if token_stream.size.zero? or token_stream.size.odd?
115
+ raise Error, "#{token_stream.inspect} is not a valid token stream"
116
+ end
107
117
  token_stream.in_groups_of(2)
108
118
  end
109
119
 
@@ -32,12 +32,16 @@ module Ultrasphinx
32
32
 
33
33
  MAX_INT = 2**32-1
34
34
 
35
- MAX_WORDS = 2**16 # maximum number of stopwords built
35
+ MAX_WORDS = 2**16 # The maximum number of stopwords built
36
36
 
37
- UNIFIED_INDEX_NAME = "complete"
37
+ MAIN_INDEX = "main"
38
+
39
+ DELTA_INDEX = "delta"
40
+
41
+ INDEXES = [MAIN_INDEX, DELTA_INDEX]
38
42
 
39
43
  CONFIG_MAP = {
40
- # These must be symbols for key mapping against Rails itself
44
+ # These must be symbols for key mapping against Rails itself.
41
45
  :username => 'sql_user',
42
46
  :password => 'sql_pass',
43
47
  :host => 'sql_host',
@@ -59,12 +63,14 @@ module Ultrasphinx
59
63
  SQL_FUNCTIONS = {
60
64
  'mysql' => {
61
65
  'group_concat' => "CAST(GROUP_CONCAT(DISTINCT ? SEPARATOR ' ') AS CHAR)",
66
+ 'delta' => "DATE_SUB(NOW(), INTERVAL ? SECOND)",
62
67
  'hash' => "CAST(CRC32(?) AS unsigned)",
63
68
  'range_cast' => "?",
64
69
  'stored_procedures' => {}
65
70
  },
66
71
  'postgresql' => {
67
72
  'group_concat' => "GROUP_CONCAT(?)",
73
+ 'delta' => "(NOW() - '? SECOND'::interval)",
68
74
  'range_cast' => "cast(coalesce(?,1) AS integer)",
69
75
  'hash' => "CRC32(?)",
70
76
  'stored_procedures' => Hash[*(
@@ -90,7 +96,8 @@ sql_query_pre = ) + SQL_FUNCTIONS['postgresql']['stored_procedures'].values.join
90
96
 
91
97
  ADAPTER = ActiveRecord::Base.connection.instance_variable_get("@config")[:adapter] rescue 'mysql'
92
98
 
93
- # Install the stored procedures
99
+ # Install the stored procedures.
100
+ # XXX This shouldn't be done at every index, say the Postgres people.
94
101
  SQL_FUNCTIONS[ADAPTER]['stored_procedures'].each do |key, value|
95
102
  ActiveRecord::Base.connection.execute(value)
96
103
  end
@@ -106,34 +113,71 @@ sql_query_pre = ) + SQL_FUNCTIONS['postgresql']['stored_procedures'].values.join
106
113
  else
107
114
  STDERR.puts msg
108
115
  end
109
- end
116
+ end
117
+ nil # Explicitly return nil
110
118
  end
111
119
 
112
120
  # Configuration file parser.
113
121
  def self.options_for(heading, path)
114
- section = open(path).read[/^#{heading.gsub('/', '__')}\s*?\{(.*?)\}/m, 1]
122
+ # Evaluate ERB
123
+ template = ERB.new(File.open(path) {|f| f.read})
124
+ contents = template.result(binding)
115
125
 
116
- unless section
117
- Ultrasphinx.say "warning; heading #{heading} not found in #{path}; it may be corrupted. "
118
- {}
119
- else
126
+ # Find the correct heading.
127
+ section = contents[/^#{heading.gsub('/', '__')}\s*?\{(.*?)\}/m, 1]
128
+
129
+ if section
130
+ # Convert to a hash
120
131
  options = section.split("\n").map do |line|
121
132
  line =~ /\s*(.*?)\s*=\s*([^\#]*)/
122
133
  $1 ? [$1, $2.strip] : []
123
134
  end
124
135
  Hash[*options.flatten]
125
- end
126
-
136
+ else
137
+ # XXX Is it safe to raise here?
138
+ Ultrasphinx.say "warning; heading #{heading} not found in #{path}; it may be corrupted. "
139
+ {}
140
+ end
127
141
  end
142
+
143
+ def self.get_models_to_class_ids #:nodoc:
144
+ # Reading the conf file makes sure that we are in sync with the actual Sphinx index, not
145
+ # whatever you happened to change your models to most recently.
146
+ if File.exist? CONF_PATH
147
+ lines, hash = open(CONF_PATH).readlines, {}
148
+ msg = "#{CONF_PATH} file is corrupted. Please run 'rake ultrasphinx:configure'."
149
+
150
+ lines.each_with_index do |line, index|
151
+ # Find the main sources
152
+ if line =~ /^source ([\w\d_-]*)_#{MAIN_INDEX}/
153
+ # Derive the model name
154
+ model = $1.gsub('__', '/').classify
128
155
 
129
- # Introspect on the existing generated conf files
156
+ # Get the id modulus out of the adjacent sql_query
157
+ query = lines[index..-1].detect do |query_line|
158
+ query_line =~ /^sql_query /
159
+ end
160
+ raise ConfigurationError, msg unless query
161
+ hash[model] = query[/(\d*) AS class_id/, 1].to_i
162
+ end
163
+ end
164
+ raise ConfigurationError, msg unless hash.values.size == hash.values.uniq.size
165
+ hash
166
+ else
167
+ # We can't raise here because you may be generating the configuration for the first time
168
+ Ultrasphinx.say "configuration file not found for #{RAILS_ENV.inspect} environment"
169
+ Ultrasphinx.say "please run 'rake ultrasphinx:configure'"
170
+ end
171
+ end
172
+
173
+ # Introspect on the existing generated conf files.
130
174
  INDEXER_SETTINGS = options_for('indexer', BASE_PATH)
131
175
  CLIENT_SETTINGS = options_for('client', BASE_PATH)
132
176
  DAEMON_SETTINGS = options_for('searchd', BASE_PATH)
133
177
  SOURCE_SETTINGS = options_for('source', BASE_PATH)
134
178
  INDEX_SETTINGS = options_for('index', BASE_PATH)
135
179
 
136
- # Make sure there's a trailing slash
180
+ # Make sure there's a trailing slash.
137
181
  INDEX_SETTINGS['path'] = INDEX_SETTINGS['path'].chomp("/") + "/"
138
182
 
139
183
  DICTIONARY = CLIENT_SETTINGS['dictionary_name'] || 'ap'
@@ -148,7 +192,7 @@ sql_query_pre = ) + SQL_FUNCTIONS['postgresql']['stored_procedures'].values.join
148
192
  if File.exist? CONF_PATH
149
193
  begin
150
194
  if options_for(
151
- "source #{MODEL_CONFIGURATION.keys.first.tableize}",
195
+ "source #{MODEL_CONFIGURATION.keys.first.tableize}_#{MAIN_INDEX}",
152
196
  CONF_PATH
153
197
  )['sql_db'] != ActiveRecord::Base.connection.instance_variable_get("@config")[:database]
154
198
  say "warning; configured database name is out-of-date"
@@ -158,5 +202,14 @@ sql_query_pre = ) + SQL_FUNCTIONS['postgresql']['stored_procedures'].values.join
158
202
  end
159
203
  end
160
204
  end
161
-
205
+
206
+ # See if a delta index was defined.
207
+ def self.delta_index_present?
208
+ if File.exist?(CONF_PATH)
209
+ File.open(CONF_PATH).readlines.detect do |line|
210
+ line =~ /^index delta/
211
+ end
212
+ end
213
+ end
214
+
162
215
  end
@@ -12,7 +12,7 @@ namespace :ultrasphinx do
12
12
  desc "Bootstrap a full Sphinx environment"
13
13
  task :bootstrap => [:_environment, :configure, :index, :"daemon:restart"] do
14
14
  say "done"
15
- say "please restart Mongrel"
15
+ say "please restart your application containers"
16
16
  end
17
17
 
18
18
  desc "Rebuild the configuration file for this particular environment."
@@ -20,31 +20,23 @@ namespace :ultrasphinx do
20
20
  Ultrasphinx::Configure.run
21
21
  end
22
22
 
23
- desc "Reindex the database and send an update signal to the search daemon."
24
- task :index => [:_environment] do
25
- rotate = ultrasphinx_daemon_running?
26
- mkdir_p Ultrasphinx::INDEX_SETTINGS['path']
27
-
28
- cmd = "indexer --config '#{Ultrasphinx::CONF_PATH}'"
29
- cmd << " #{ENV['OPTS']} " if ENV['OPTS']
30
- cmd << " --rotate" if rotate
31
- cmd << " #{Ultrasphinx::UNIFIED_INDEX_NAME}"
32
-
33
- say cmd
34
- system cmd
35
-
36
- if rotate
37
- sleep(4)
38
- failed = Dir[Ultrasphinx::INDEX_SETTINGS['path'] + "/*.new.*"]
39
- if failed.any?
40
- say "warning; index failed to rotate! Deleting new indexes"
41
- failed.each {|f| File.delete f }
42
- else
43
- say "index rotated ok"
44
- end
23
+ namespace :index do
24
+ desc "Reindex and rotate the main index."
25
+ task :main => [:_environment] do
26
+ ultrasphinx_index(Ultrasphinx::MAIN_INDEX)
45
27
  end
28
+
29
+ desc "Reindex and rotate the delta index."
30
+ task :delta => [:_environment] do
31
+ ultrasphinx_index(Ultrasphinx::DELTA_INDEX)
32
+ end
33
+
34
+ end
35
+
36
+ desc "Reindex and rotate all indexes."
37
+ task :index => [:_environment] do
38
+ ultrasphinx_index("--all")
46
39
  end
47
-
48
40
 
49
41
  namespace :daemon do
50
42
  desc "Start the search daemon"
@@ -125,30 +117,56 @@ namespace :us do
125
117
  task :restart => ["ultrasphinx:daemon:restart"]
126
118
  task :stop => ["ultrasphinx:daemon:stop"]
127
119
  task :stat => ["ultrasphinx:daemon:status"]
120
+ task :index => ["ultrasphinx:index"]
128
121
  task :in => ["ultrasphinx:index"]
122
+ task :main => ["ultrasphinx:index:main"]
123
+ task :delta => ["ultrasphinx:index:delta"]
129
124
  task :spell => ["ultrasphinx:spelling:build"]
130
125
  task :conf => ["ultrasphinx:configure"]
131
126
  task :boot => ["ultrasphinx:bootstrap"]
132
127
  end
133
128
 
134
- # support methods
129
+ # Support methods
135
130
 
136
131
  def ultrasphinx_daemon_pid
137
- open(open(Ultrasphinx::BASE_PATH).readlines.map do |line|
138
- line[/^\s*pid_file\s*=\s*([^\s\#]*)/, 1]
139
- end.compact.first).readline.chomp rescue nil # XXX ridiculous
132
+ open(Ultrasphinx::DAEMON_SETTINGS['pid_file']).readline.chomp rescue nil
140
133
  end
141
134
 
142
135
  def ultrasphinx_daemon_running?
143
136
  if ultrasphinx_daemon_pid and `ps #{ultrasphinx_daemon_pid} | wc`.to_i > 1
144
137
  true
145
138
  else
146
- # remove bogus lockfiles
139
+ # Remove bogus lockfiles.
147
140
  Dir[Ultrasphinx::INDEX_SETTINGS["path"] + "*spl"].each {|file| File.delete(file)}
148
141
  false
149
142
  end
150
143
  end
151
144
 
145
+ def ultrasphinx_index(index)
146
+ rotate = ultrasphinx_daemon_running?
147
+ index_path = Ultrasphinx::INDEX_SETTINGS['path']
148
+ mkdir_p index_path unless File.directory? index_path
149
+
150
+ cmd = "indexer --config '#{Ultrasphinx::CONF_PATH}'"
151
+ cmd << " #{ENV['OPTS']} " if ENV['OPTS']
152
+ cmd << " --rotate" if rotate
153
+ cmd << " #{index}"
154
+
155
+ say "$ #{cmd}"
156
+ system cmd
157
+
158
+ if rotate
159
+ sleep(4)
160
+ failed = Dir[index_path + "/*.new.*"]
161
+ if failed.any?
162
+ say "warning; index failed to rotate! Deleting new indexes"
163
+ failed.each {|f| File.delete f }
164
+ else
165
+ say "index rotated ok"
166
+ end
167
+ end
168
+ end
169
+
152
170
  def say msg
153
171
  Ultrasphinx.say msg
154
172
  end
@@ -2,55 +2,84 @@
2
2
  #
3
3
  # Sphinx/Ultrasphinx user-configurable options.
4
4
  #
5
- # Copy this file to RAILS_ROOT/config/ultrasphinx.
6
- # You can use individual namespaces if you want (e.g. "development.base").
5
+ # Copy this file to RAILS_ROOT/config/ultrasphinx. You can use individual
6
+ # namespaces if you want (e.g. development.base, production.base,
7
+ # test.base). Note that ERb is also allowed.
8
+ #
9
+ # This file should not be handed directly to Sphinx. Use the rake task
10
+ #
11
+ # rake ultrasphinx::configure
12
+ #
13
+ # to generate a parallel default.conf file. This is the file that Sphinx itself will
14
+ # use. The Ultrasphinx rake tasks automatically pass the correct file to
15
+ # to Sphinx.
16
+ #
17
+ # It is safe to edit .base files by hand. It is not safe to edit the generated
18
+ # .conf files. Do not symlink the .conf file to the .base file; it's wrong.
7
19
  #
8
20
 
21
+ <% path = "/opt/local/var/db/sphinx/" %>
22
+
23
+ # Indexing options
9
24
  indexer
10
- {
11
- # Indexer running options
25
+ {
12
26
  mem_limit = 256M
27
+
28
+ # Ultrasphinx-specific key
29
+ delta = <%= 1.day + 30.minutes %>
13
30
  }
14
31
 
32
+ # Daemon options
15
33
  searchd
16
- {
17
- # Daemon options
34
+ {
18
35
  # What interface the search daemon should listen on and where to store its logs
19
36
  address = 0.0.0.0
20
37
  port = 3312
21
- log = /opt/local/var/db/sphinx/log/searchd.log
22
- query_log = /opt/local/var/db/sphinx/log/query.log
38
+ seamless_rotate = 1
39
+ log = <%= path %>log/searchd.log
40
+ query_log = <%= path %>log/query.log
23
41
  read_timeout = 5
24
42
  max_children = 300
25
- pid_file = /opt/local/var/db/sphinx/log/searchd.pid
43
+ pid_file = <%= path %>log/searchd.pid
26
44
  max_matches = 100000
27
45
  }
28
46
 
47
+ # Client options
29
48
  client
30
49
  {
31
- # Client options
50
+ # Name of the Aspell dictionary (two letters max)
51
+ dictionary_name = ap
52
+
32
53
  # How your application connects to the search daemon (not necessarily the same as above)
33
54
  server_host = localhost
34
55
  server_port = 3312
35
56
  }
36
57
 
58
+ # Individual SQL source options
37
59
  source
38
- {
39
- # Individual SQL source options
40
- sql_range_step = 20000
41
- strip_html = 0
42
- index_html_attrs =
60
+ {
61
+ sql_ranged_throttle = 0
62
+ sql_range_step = 5000
43
63
  sql_query_post =
44
64
  }
45
65
 
66
+ # Index building options
46
67
  index
47
- {
48
- # Index building options
49
- path = /opt/local/var/db/sphinx/
50
- docinfo = extern # just leave this alone
68
+ {
69
+ path = <%= path %>
70
+ docinfo = extern # Just leave this alone
51
71
  morphology = stem_en
52
- stopwords = # /path/to/stopwords.txt
72
+ stopwords = # <%= path %>/ap-stopwords.txt
53
73
  min_word_len = 1
74
+
75
+ # HTML-specific options
76
+ html_strip = 0
77
+ html_index_attrs =
78
+
79
+ # Enable these if you need wildcard searching. They will slow down indexing significantly.
80
+ # min_infix_len = 1
81
+ # enable_star = 1
82
+
54
83
  charset_type = utf-8 # or sbcs (Single Byte Character Set)
55
- charset_table = 0..9, A..Z->a..z, -, _, ., &, a..z,
84
+ charset_table = 0..9, A..Z->a..z, -, _, ., &, a..z, U+410..U+42F->U+430..U+44F, U+430..U+44F,U+C5->U+E5, U+E5, U+C4->U+E4, U+E4, U+D6->U+F6, U+F6, U+16B, U+0c1->a, U+0c4->a, U+0c9->e, U+0cd->i, U+0d3->o, U+0d4->o, U+0da->u, U+0dd->y, U+0e1->a, U+0e4->a, U+0e9->e, U+0ed->i, U+0f3->o, U+0f4->o, U+0fa->u, U+0fd->y, U+104->U+105, U+105, U+106->U+107, U+10c->c, U+10d->c, U+10e->d, U+10f->d, U+116->U+117, U+117, U+118->U+119, U+11a->e, U+11b->e, U+12E->U+12F, U+12F, U+139->l, U+13a->l, U+13d->l, U+13e->l, U+141->U+142, U+142, U+143->U+144, U+144,U+147->n, U+148->n, U+154->r, U+155->r, U+158->r, U+159->r, U+15A->U+15B, U+15B, U+160->s, U+160->U+161, U+161->s, U+164->t, U+165->t, U+16A->U+16B, U+16B, U+16e->u, U+16f->u, U+172->U+173, U+173, U+179->U+17A, U+17A, U+17B->U+17C, U+17C, U+17d->z, U+17e->z,
56
85
  }
@@ -4,5 +4,6 @@ class Geo::Address < ActiveRecord::Base
4
4
 
5
5
  is_indexed 'fields' => ['name'],
6
6
  'concatenate' => [{'fields' => ['line_1', 'line_2', 'city', 'province_region', 'zip_postal_code'], 'as' => 'content'}],
7
- 'include' => [{'association_name' => 'state', 'field' => 'name', 'as' => 'state'}]
7
+ 'include' => [{'association_name' => 'state', 'field' => 'name', 'as' => 'state'}],
8
+ 'delta' => true
8
9
  end
@@ -3,8 +3,17 @@ class User < ActiveRecord::Base
3
3
  has_one :address, :class_name => "Geo::Address"
4
4
 
5
5
  is_indexed :fields => ['login', 'email', 'deleted'],
6
- :include => [{:association_name => 'specific_seller', :field => 'company_name', :as => 'company'},
7
- {:class_name => 'Seller', :field => 'sellers_two.company_name', :as => 'company_two', 'association_sql' => 'LEFT OUTER JOIN sellers AS sellers_two ON users.id = sellers_two.user_id', 'function_sql' => "REPLACE(?, '6', ' replacement ')"}],
8
- :conditions => "deleted = '0'"
6
+ :include => [{:association_name => 'specific_seller', :field => 'company_name', :as => 'company', :facet => true},
7
+ {:class_name => 'Seller', :field => 'sellers_two.company_name', :as => 'company_two', :facet => true, 'association_sql' => 'LEFT OUTER JOIN sellers AS sellers_two ON users.id = sellers_two.user_id', 'function_sql' => "REPLACE(?, '6', ' replacement ')"}],
8
+ :conditions => "deleted = '0'",
9
+ :delta => {:field => 'created_at'}
10
+
11
+ def self.find_all_by_id(*args)
12
+ raise "Wrong finder"
13
+ end
14
+
15
+ def self.custom_find(*args)
16
+ method_missing(:find_all_by_id, *args)
17
+ end
9
18
 
10
19
  end