ultrasphinx 1.9 → 1.11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (111) hide show
  1. data.tar.gz.sig +0 -0
  2. data/CHANGELOG +6 -0
  3. data/DEPLOYMENT_NOTES +3 -1
  4. data/Manifest +10 -2
  5. data/RAKE_TASKS +2 -1
  6. data/README +6 -6
  7. data/Rakefile +27 -0
  8. data/examples/default.base +7 -2
  9. data/init.rb +1 -0
  10. data/lib/ultrasphinx/autoload.rb +0 -1
  11. data/lib/ultrasphinx/configure.rb +64 -36
  12. data/lib/ultrasphinx/core_extensions.rb +17 -2
  13. data/lib/ultrasphinx/fields.rb +33 -13
  14. data/lib/ultrasphinx/is_indexed.rb +32 -14
  15. data/lib/ultrasphinx/postgresql/concat_ws.sql +3 -3
  16. data/lib/ultrasphinx/postgresql/crc32.sql +1 -1
  17. data/lib/ultrasphinx/postgresql/group_concat.sql +1 -3
  18. data/lib/ultrasphinx/postgresql/hex_to_int.sql +1 -1
  19. data/lib/ultrasphinx/postgresql/unix_timestamp.sql +1 -1
  20. data/lib/ultrasphinx/search.rb +56 -6
  21. data/lib/ultrasphinx/search/internals.rb +88 -23
  22. data/lib/ultrasphinx/search/parser.rb +16 -2
  23. data/lib/ultrasphinx/spell.rb +2 -1
  24. data/lib/ultrasphinx/ultrasphinx.rb +43 -49
  25. data/tasks/ultrasphinx.rake +56 -16
  26. data/test/config/ultrasphinx/test.base +7 -2
  27. data/test/integration/app/app/controllers/addresses_controller.rb +9 -9
  28. data/test/integration/app/app/controllers/states_controller.rb +9 -9
  29. data/test/integration/app/app/models/category.rb +5 -0
  30. data/test/integration/app/app/models/geo/address.rb +1 -1
  31. data/test/integration/app/app/models/seller.rb +3 -3
  32. data/test/integration/app/app/views/addresses/edit.html.erb +2 -2
  33. data/test/integration/app/app/views/addresses/index.html.erb +2 -2
  34. data/test/integration/app/app/views/addresses/new.html.erb +1 -1
  35. data/test/integration/app/app/views/states/edit.html.erb +2 -2
  36. data/test/integration/app/app/views/states/index.html.erb +2 -2
  37. data/test/integration/app/app/views/states/new.html.erb +1 -1
  38. data/test/integration/app/app/views/users/index.html.erb +3 -3
  39. data/test/integration/app/config/environment.rb +1 -0
  40. data/test/integration/app/config/ultrasphinx/default.base +2 -2
  41. data/test/integration/app/config/ultrasphinx/development.conf.canonical +74 -50
  42. data/test/integration/app/db/migrate/007_add_lat_and_long_to_address.rb +3 -3
  43. data/test/integration/app/db/migrate/010_create_categories.rb +14 -0
  44. data/test/integration/app/db/migrate/011_categories_sellers.rb +15 -0
  45. data/test/integration/app/public/dispatch.cgi +0 -0
  46. data/test/integration/app/public/dispatch.fcgi +0 -0
  47. data/test/integration/app/public/dispatch.rb +0 -0
  48. data/test/integration/app/script/about +0 -0
  49. data/test/integration/app/script/breakpointer +0 -0
  50. data/test/integration/app/script/console +0 -0
  51. data/test/integration/app/script/destroy +0 -0
  52. data/test/integration/app/script/generate +0 -0
  53. data/test/integration/app/script/performance/benchmarker +0 -0
  54. data/test/integration/app/script/performance/profiler +0 -0
  55. data/test/integration/app/script/plugin +0 -0
  56. data/test/integration/app/script/process/inspector +0 -0
  57. data/test/integration/app/script/process/reaper +0 -0
  58. data/test/integration/app/script/process/spawner +0 -0
  59. data/test/integration/app/script/runner +0 -0
  60. data/test/integration/app/script/server +0 -0
  61. data/test/integration/app/test/fixtures/addresses.yml +77 -6
  62. data/test/integration/app/test/fixtures/categories.yml +101 -0
  63. data/test/integration/app/test/fixtures/categories_sellers.yml +29 -0
  64. data/test/integration/app/test/functional/addresses_controller_test.rb +9 -4
  65. data/test/integration/app/test/functional/sellers_controller_test.rb +9 -2
  66. data/test/integration/app/test/functional/states_controller_test.rb +10 -4
  67. data/test/integration/app/test/functional/users_controller_test.rb +7 -2
  68. data/test/integration/app/test/unit/address_test.rb +1 -1
  69. data/test/integration/app/test/unit/category_test.rb +8 -0
  70. data/test/integration/app/test/unit/country_test.rb +1 -1
  71. data/test/integration/app/test/unit/state_test.rb +1 -1
  72. data/test/integration/app/test/unit/user_test.rb +1 -1
  73. data/test/integration/delta_test.rb +13 -0
  74. data/test/integration/search_test.rb +80 -8
  75. data/test/profile/benchmark.rb +0 -0
  76. data/test/setup.rb +25 -7
  77. data/test/teardown.rb +13 -0
  78. data/test/test_helper.rb +3 -3
  79. data/ultrasphinx.gemspec +22 -43
  80. data/vendor/riddle/README +18 -4
  81. data/vendor/riddle/Rakefile +1 -0
  82. data/vendor/riddle/lib/riddle.rb +11 -5
  83. data/vendor/riddle/lib/riddle/client.rb +65 -20
  84. data/vendor/riddle/lib/riddle/client/response.rb +10 -0
  85. data/vendor/riddle/spec/fixtures/data/anchor.bin +0 -0
  86. data/vendor/riddle/spec/fixtures/data/any.bin +0 -0
  87. data/vendor/riddle/spec/fixtures/data/boolean.bin +0 -0
  88. data/vendor/riddle/spec/fixtures/data/comment.bin +0 -0
  89. data/vendor/riddle/spec/fixtures/data/distinct.bin +0 -0
  90. data/vendor/riddle/spec/fixtures/data/field_weights.bin +0 -0
  91. data/vendor/riddle/spec/fixtures/data/filter.bin +0 -0
  92. data/vendor/riddle/spec/fixtures/data/group.bin +0 -0
  93. data/vendor/riddle/spec/fixtures/data/index.bin +0 -0
  94. data/vendor/riddle/spec/fixtures/data/index_weights.bin +0 -0
  95. data/vendor/riddle/spec/fixtures/data/keywords_with_hits.bin +0 -0
  96. data/vendor/riddle/spec/fixtures/data/keywords_without_hits.bin +0 -0
  97. data/vendor/riddle/spec/fixtures/data/phrase.bin +0 -0
  98. data/vendor/riddle/spec/fixtures/data/rank_mode.bin +0 -0
  99. data/vendor/riddle/spec/fixtures/data/simple.bin +0 -0
  100. data/vendor/riddle/spec/fixtures/data/sort.bin +0 -0
  101. data/vendor/riddle/spec/fixtures/data/weights.bin +0 -0
  102. data/vendor/riddle/spec/fixtures/data_generator.php +15 -0
  103. data/vendor/riddle/spec/fixtures/sphinx/configuration.erb +4 -4
  104. data/vendor/riddle/spec/fixtures/sphinxapi.php +118 -7
  105. data/vendor/riddle/spec/functional/keywords_spec.rb +40 -0
  106. data/vendor/riddle/spec/spec_helper.rb +1 -0
  107. data/vendor/riddle/spec/unit/client_spec.rb +26 -0
  108. metadata +38 -11
  109. metadata.gz.sig +0 -0
  110. data/test/integration/app/config/ultrasphinx/development.conf +0 -319
  111. data/test/integration/app/db/schema.rb +0 -56
data.tar.gz.sig CHANGED
Binary file
data/CHANGELOG CHANGED
@@ -1,4 +1,10 @@
1
1
 
2
+ v1.11. Update to Sphinx 0.9.8-rc2.
3
+
4
+ v1.10. Add geodistance support (Jeremy Seitz, Mark Lane). Move Postgres stored procedures into a migration generator (Lang Riley). Duplicate text :sortable columns so that they are also searchable.
5
+
6
+ v1.9.1. Add ultrasphinx:index:merge task for index merging, and a note in DEPLOYMENT_NOTES about how to use it.
7
+
2
8
  v1.9. Delta indexing. ERb now supported in .base files. Allow setting the searched indexes at runtime.
3
9
 
4
10
  v1.8.1. Use multifind/multiget for record loading; avoid using HashWithIndifferentAccess internally for speed; other minor performance improvements.
@@ -17,13 +17,15 @@ It's easy to keep the search daemon and the indexer running in a production envi
17
17
 
18
18
  */6 * * * * bash -c 'cd /path/to/production/current/; RAILS_ENV=production \
19
19
  rake ultrasphinx:index:delta >> /log/ultrasphinx-index.log 2>&1'
20
- 1 4 * * * * bash -c 'cd /path/to/production/current/; RAILS_ENV=production \
20
+ 1 4 * * * bash -c 'cd /path/to/production/current/; RAILS_ENV=production \
21
21
  rake ultrasphinx:index:main >> /log/ultrasphinx-index.log 2>&1'
22
22
  */3 * * * * bash -c 'cd /path/to/production/current/; RAILS_ENV=production \
23
23
  rake ultrasphinx:daemon:start >> /log/ultrasphinx-daemon.log 2>&1'
24
24
 
25
25
  The first line reindexes the delta index every 10 minutes. The second line reindexes the main index once a day at 4am. The third line will try to restart the search daemon every three minutes. If it's already running, nothing happens.
26
26
 
27
+ Note that you can use <tt>ultrasphinx:index:merge</tt> in place of <tt>ultrasphinx:index:main</tt>. It will be faster, but will not remove deleted records from the index.
28
+
27
29
  Of course if you don't have any models with deltas, don't include the <tt>ultrasphinx:index:delta</tt> task.
28
30
 
29
31
  If you are under severe memory limitations you might want to manage the daemon with Monit instead, so you can keep a closer eye on it. The search daemon is extremely reliable, so don't bother with fancy monitoring infrastructure unless you're sure you need it.
data/Manifest CHANGED
@@ -37,6 +37,7 @@ test/integration/app/app/helpers/application_helper.rb
37
37
  test/integration/app/app/helpers/sellers_helper.rb
38
38
  test/integration/app/app/helpers/states_helper.rb
39
39
  test/integration/app/app/helpers/users_helper.rb
40
+ test/integration/app/app/models/category.rb
40
41
  test/integration/app/app/models/geo/address.rb
41
42
  test/integration/app/app/models/geo/country.rb
42
43
  test/integration/app/app/models/geo/state.rb
@@ -71,7 +72,6 @@ test/integration/app/config/environments/test.rb
71
72
  test/integration/app/config/locomotive.yml
72
73
  test/integration/app/config/routes.rb
73
74
  test/integration/app/config/ultrasphinx/default.base
74
- test/integration/app/config/ultrasphinx/development.conf
75
75
  test/integration/app/config/ultrasphinx/development.conf.canonical
76
76
  test/integration/app/db/migrate/001_create_users.rb
77
77
  test/integration/app/db/migrate/002_create_sellers.rb
@@ -82,7 +82,8 @@ test/integration/app/db/migrate/006_add_deleted_to_user.rb
82
82
  test/integration/app/db/migrate/007_add_lat_and_long_to_address.rb
83
83
  test/integration/app/db/migrate/008_add_mission_statement_to_seller.rb
84
84
  test/integration/app/db/migrate/009_create_countries.rb
85
- test/integration/app/db/schema.rb
85
+ test/integration/app/db/migrate/010_create_categories.rb
86
+ test/integration/app/db/migrate/011_categories_sellers.rb
86
87
  test/integration/app/doc/README_FOR_APP
87
88
  test/integration/app/public/404.html
88
89
  test/integration/app/public/500.html
@@ -115,6 +116,8 @@ test/integration/app/script/process/spawner
115
116
  test/integration/app/script/runner
116
117
  test/integration/app/script/server
117
118
  test/integration/app/test/fixtures/addresses.yml
119
+ test/integration/app/test/fixtures/categories.yml
120
+ test/integration/app/test/fixtures/categories_sellers.yml
118
121
  test/integration/app/test/fixtures/countries.yml
119
122
  test/integration/app/test/fixtures/sellers.yml
120
123
  test/integration/app/test/fixtures/states.yml
@@ -125,6 +128,7 @@ test/integration/app/test/functional/states_controller_test.rb
125
128
  test/integration/app/test/functional/users_controller_test.rb
126
129
  test/integration/app/test/test_helper.rb
127
130
  test/integration/app/test/unit/address_test.rb
131
+ test/integration/app/test/unit/category_test.rb
128
132
  test/integration/app/test/unit/country_test.rb
129
133
  test/integration/app/test/unit/seller_test.rb
130
134
  test/integration/app/test/unit/state_test.rb
@@ -153,6 +157,7 @@ vendor/riddle/README
153
157
  vendor/riddle/spec/fixtures/data/anchor.bin
154
158
  vendor/riddle/spec/fixtures/data/any.bin
155
159
  vendor/riddle/spec/fixtures/data/boolean.bin
160
+ vendor/riddle/spec/fixtures/data/comment.bin
156
161
  vendor/riddle/spec/fixtures/data/distinct.bin
157
162
  vendor/riddle/spec/fixtures/data/field_weights.bin
158
163
  vendor/riddle/spec/fixtures/data/filter.bin
@@ -166,6 +171,8 @@ vendor/riddle/spec/fixtures/data/filter_range_exclude.bin
166
171
  vendor/riddle/spec/fixtures/data/group.bin
167
172
  vendor/riddle/spec/fixtures/data/index.bin
168
173
  vendor/riddle/spec/fixtures/data/index_weights.bin
174
+ vendor/riddle/spec/fixtures/data/keywords_with_hits.bin
175
+ vendor/riddle/spec/fixtures/data/keywords_without_hits.bin
169
176
  vendor/riddle/spec/fixtures/data/phrase.bin
170
177
  vendor/riddle/spec/fixtures/data/rank_mode.bin
171
178
  vendor/riddle/spec/fixtures/data/simple.bin
@@ -179,6 +186,7 @@ vendor/riddle/spec/fixtures/sql/conf.example.yml
179
186
  vendor/riddle/spec/fixtures/sql/data.sql
180
187
  vendor/riddle/spec/fixtures/sql/structure.sql
181
188
  vendor/riddle/spec/functional/excerpt_spec.rb
189
+ vendor/riddle/spec/functional/keywords_spec.rb
182
190
  vendor/riddle/spec/functional/search_spec.rb
183
191
  vendor/riddle/spec/functional/update_spec.rb
184
192
  vendor/riddle/spec/spec_helper.rb
data/RAKE_TASKS CHANGED
@@ -7,6 +7,7 @@ These Rake tasks are made available to your Rails app:
7
7
  <tt>ultrasphinx:index</tt>:: Reindex and rotate all indexes.
8
8
  <tt>ultrasphinx:index:delta</tt>:: Reindex and rotate the delta index.
9
9
  <tt>ultrasphinx:index:main</tt>:: Reindex and rotate the main index.
10
+ <tt>ultrasphinx:index:merge</tt>:: Merge the delta index into the main index.
10
11
  <tt>ultrasphinx:daemon:restart</tt>:: Restart the search daemon.
11
12
  <tt>ultrasphinx:daemon:start</tt>:: Start the search daemon.
12
13
  <tt>ultrasphinx:daemon:stop</tt>:: Stop the search daemon.
@@ -14,4 +15,4 @@ These Rake tasks are made available to your Rails app:
14
15
  <tt>ultrasphinx:spelling:build</tt>:: Rebuild the custom spelling dictionary. You may need to use <tt>sudo</tt> if your Aspell folder is not writable by the app user.
15
16
  <tt>ultrasphinx:bootstrap</tt>:: Bootstrap a full Sphinx environment by running configure, index, then daemon:start.
16
17
 
17
- All tasks have shortcuts. Use <tt>us:conf</tt>, <tt>us:index</tt>, <tt>us:main</tt>, <tt>us:delta</tt>, <tt>us:restart</tt>, <tt>us:start</tt>, <tt>us:stop</tt>, <tt>us:stat</tt>, <tt>us:spell</tt>, and <tt>us:boot</tt>.
18
+ All tasks have shortcuts. Use <tt>us:conf</tt>, <tt>us:index</tt>, <tt>us:main</tt>, <tt>us:delta</tt>, <tt>us:merge</tt>, <tt>us:restart</tt>, <tt>us:start</tt>, <tt>us:stop</tt>, <tt>us:stat</tt>, <tt>us:spell</tt>, and <tt>us:boot</tt>.
data/README CHANGED
@@ -1,4 +1,3 @@
1
-
2
1
  Ultrasphinx
3
2
 
4
3
  Ruby on Rails configurator and client to the Sphinx full text search engine.
@@ -14,7 +13,7 @@ If you use this software, please {make a donation}[http://blog.evanweaver.com/do
14
13
  == Requirements
15
14
 
16
15
  * MySQL 5.0, or PostgreSQL 8.2
17
- * Sphinx 0.9.8-rc1
16
+ * Sphinx 0.9.8-rc2
18
17
  * Rails 2.0.2
19
18
 
20
19
  More recent versions than listed are usually ok.
@@ -25,13 +24,14 @@ Sphinx/Ultrasphinx is the fastest and most stable Rails fulltext search solution
25
24
 
26
25
  Features include:
27
26
 
28
- * searching and ranking across orthogonal models
27
+ * searching and ranking across multiple models
29
28
  * delta index support
30
29
  * excerpt highlighting
31
30
  * Google-style query parser
32
31
  * spellcheck
33
32
  * faceting on text, date, and numeric fields
34
- * field weighting, merging, and aliases
33
+ * field weighting, merging, and aliasing
34
+ * geodistance
35
35
  * <tt>belongs_to</tt> and <tt>has_many</tt> includes
36
36
  * drop-in compatibility with will_paginate[http://err.lighthouseapp.com/projects/466/home]
37
37
  * drop-in compatibility with Interlock[http://blog.evanweaver.com/files/doc/fauna/interlock/]
@@ -50,7 +50,7 @@ You also need the <tt>chronic</tt> gem:
50
50
  sudo gem install chronic
51
51
 
52
52
  Then, install the plugin:
53
- script/plugin install -x svn://rubyforge.org/var/svn/fauna/ultrasphinx/trunk
53
+ script/plugin install git://github.com/fauna/ultrasphinx.git
54
54
 
55
55
  Next, copy the <tt>examples/default.base</tt> file to <tt>RAILS_ROOT/config/ultrasphinx/default.base</tt>. This file sets up the Sphinx daemon options such as port, host, and index location.
56
56
 
@@ -124,7 +124,7 @@ See DEPLOYMENT_NOTES[link:files/DEPLOYMENT_NOTES.html].
124
124
 
125
125
  Note that since Ultrasphinx preloads indexed models, you need to make sure those models have their own dependencies in place early in the boot process. This may require adjusting the general plugin load order or moving monkey-patches from <tt>lib/</tt> to <tt>vendor/plugins/</tt>.
126
126
 
127
- PostgreSQL 8.2 and higher are well supported. However, make sure you have executed <tt>CREATE LANGUAGE plpgsql;</tt> at least once. This step does not need to be repeated, so depending on your DB permissions, you might be able to put it in a migration.
127
+ PostgreSQL 8.2 and higher are well supported. However, make sure the stored procedure migration gets generated correctly. If you did not install the plugin via <tt>script/install</tt>, run <tt>script/generate ultrasphinx_migration</tt> by hand.
128
128
 
129
129
  == Reporting problems
130
130
 
@@ -0,0 +1,27 @@
1
+
2
+ require 'echoe'
3
+
4
+ Echoe.new("ultrasphinx") do |p|
5
+ p.project = "fauna"
6
+ p.summary = "Ruby on Rails configurator and client to the Sphinx fulltext search engine."
7
+ p.url = "http://blog.evanweaver.com/files/doc/fauna/ultrasphinx/"
8
+ p.docs_host = "blog.evanweaver.com:~/www/bax/public/files/doc/"
9
+ p.rdoc_pattern = /is_indexed.rb|search.rb|spell.rb|ultrasphinx.rb|^README|TODO|DEPLOY|RAKE_TASKS|CHANGELOG|^LICENSE/
10
+ p.dependencies = "chronic"
11
+ p.test_pattern = ["test/integration/*.rb", "test/unit/*.rb"]
12
+ p.rcov_options << '--include-file test\/integration\/app\/vendor\/plugins\/ultrasphinx\/lib\/.*\.rb --include-file '
13
+ end
14
+
15
+ desc "Run all the tests for every database adapter"
16
+ task "test_all" do
17
+ ['mysql', 'postgresql'].each do |adapter|
18
+ ENV['DB'] = adapter
19
+ ENV['PRODUCTION'] = nil
20
+ STDERR.puts "#{'='*80}\nDevelopment mode for #{adapter}\n#{'='*80}"
21
+ system("rake test:multi_rails:all")
22
+
23
+ ENV['PRODUCTION'] = '1'
24
+ STDERR.puts "#{'='*80}\nProduction mode for #{adapter}\n#{'='*80}"
25
+ system("rake test:multi_rails:all")
26
+ end
27
+ end
@@ -34,7 +34,7 @@ searchd
34
34
  {
35
35
  # What interface the search daemon should listen on and where to store its logs
36
36
  address = 0.0.0.0
37
- port = 3312
37
+ port = 3313
38
38
  seamless_rotate = 1
39
39
  log = <%= path %>log/searchd.log
40
40
  query_log = <%= path %>log/query.log
@@ -52,7 +52,7 @@ client
52
52
 
53
53
  # How your application connects to the search daemon (not necessarily the same as above)
54
54
  server_host = localhost
55
- server_port = 3312
55
+ server_port = 3313
56
56
  }
57
57
 
58
58
  # Individual SQL source options
@@ -79,6 +79,11 @@ index
79
79
  # Enable these if you need wildcard searching. They will slow down indexing significantly.
80
80
  # min_infix_len = 1
81
81
  # enable_star = 1
82
+
83
+ # # URL search options
84
+ # # add " @, /, :," before " a-z," in the charset_table and uncomment prefix_fields
85
+ # to seach URL and email addresses
86
+ # prefix_fields = url, domain
82
87
 
83
88
  charset_type = utf-8 # or sbcs (Single Byte Character Set)
84
89
  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,
data/init.rb CHANGED
@@ -1,2 +1,3 @@
1
1
 
2
2
  require 'ultrasphinx'
3
+ require 'ultrasphinx/fields'
@@ -6,7 +6,6 @@ class Rails::Initializer
6
6
  def after_initialize_with_ultrasphinx_configuration
7
7
  after_initialize_without_ultrasphinx_configuration
8
8
  Ultrasphinx::Configure.load_constants
9
- Ultrasphinx.verify_database_name
10
9
  end
11
10
 
12
11
  alias_method_chain :after_initialize, :ultrasphinx_configuration
@@ -16,21 +16,20 @@ module Ultrasphinx
16
16
  filename = filename[0..-4]
17
17
  begin
18
18
  File.basename(filename).camelize.constantize
19
- rescue NameError => e
19
+ rescue LoadError, NameError => e
20
20
  filename.camelize.constantize
21
21
  end
22
22
  end
23
23
  rescue Object => e
24
- say "warning: possibly critical autoload error on #{filename}"
25
- say e.inspect
26
- #say e.backtrace.join("\n") if RAILS_ENV == "development"
24
+ say "warning: critical autoload error on #{filename}; try referencing \"#{filename.camelize}\" directly in the console"
25
+ say e.backtrace.join("\n") if RAILS_ENV == "development"
27
26
  end
28
27
  end
29
28
  end
30
29
  end
31
30
 
32
31
  # Build the field-to-type mappings.
33
- Fields.instance.configure(MODEL_CONFIGURATION)
32
+ Ultrasphinx::Fields.instance.configure(MODEL_CONFIGURATION)
34
33
  end
35
34
 
36
35
 
@@ -40,7 +39,9 @@ module Ultrasphinx
40
39
  load_constants
41
40
 
42
41
  say "rebuilding configurations for #{RAILS_ENV} environment"
43
- say "available models are #{MODEL_CONFIGURATION.keys.to_sentence}"
42
+ # stable sort classes by name rather than rely on hash order
43
+ model_list = MODEL_CONFIGURATION.keys.sort
44
+ say "available models are #{model_list.to_sentence}"
44
45
  File.open(CONF_PATH, "w") do |conf|
45
46
  conf.puts global_header
46
47
  say "generating SQL"
@@ -49,9 +50,8 @@ module Ultrasphinx
49
50
  sources = []
50
51
  cached_groups = Fields.instance.groups.join("\n")
51
52
 
52
- MODEL_CONFIGURATION.each_with_index do |model_and_options, class_id|
53
- # This relies on hash sort order being deterministic per-machine
54
- model, options = model_and_options
53
+ model_list.each_with_index do |model, class_id|
54
+ options = MODEL_CONFIGURATION[model]
55
55
  klass = model.constantize
56
56
  source = "#{model.tableize.gsub('/', '__')}_#{index}"
57
57
 
@@ -89,13 +89,16 @@ module Ultrasphinx
89
89
  def setup_source_database(klass)
90
90
  # Supporting Postgres now
91
91
  connection_settings = klass.connection.instance_variable_get("@config")
92
+ raise ConfigurationError, "Unsupported database adapter" unless connection_settings || defined?(JRUBY_VERSION)
92
93
 
93
94
  adapter_defaults = DEFAULTS[ADAPTER]
94
95
  raise ConfigurationError, "Unsupported database adapter" unless adapter_defaults
95
96
 
96
97
  conf = [adapter_defaults]
97
98
  connection_settings.reverse_merge(CONNECTION_DEFAULTS).each do |key, value|
98
- conf << "#{CONFIG_MAP[key]} = #{value}" if CONFIG_MAP[key]
99
+ if CONFIG_MAP[key] && !SOURCE_SETTINGS[CONFIG_MAP[key]]
100
+ conf << "#{CONFIG_MAP[key]} = #{value}"
101
+ end
99
102
  end
100
103
  conf.sort.join("\n")
101
104
  end
@@ -114,8 +117,14 @@ module Ultrasphinx
114
117
  # Warning about the sortable problem
115
118
  # XXX Kind of in an odd place, but I want to happen at index time
116
119
  Ultrasphinx.say "warning; text sortable columns on #{klass.name} will return wrong results with partial delta indexing"
117
- end
118
- string = "#{source_string} > #{SQL_FUNCTIONS[ADAPTER]['delta']._interpolate(INDEXER_SETTINGS['delta'])}";
120
+ end
121
+
122
+ delta = INDEXER_SETTINGS['delta']
123
+ if delta
124
+ string = "#{source_string} > #{SQL_FUNCTIONS[ADAPTER]['delta']._interpolate(delta)}";
125
+ else
126
+ raise ConfigurationError, "No 'indexer { delta }' setting specified in '#{BASE_PATH}'"
127
+ end
119
128
  else
120
129
  Ultrasphinx.say "warning; #{klass.name} will reindex the entire table during delta indexing"
121
130
  end
@@ -123,8 +132,18 @@ module Ultrasphinx
123
132
  end
124
133
 
125
134
 
126
- def setup_source_arrays(index, klass, fields, class_id, conditions)
127
- condition_strings = Array(conditions).map do |condition|
135
+ def setup_source_arrays(index, klass, fields, class_id, conditions, order)
136
+ if((klass.column_names.include? klass.inheritance_column) and (klass.superclass != ActiveRecord::Base))
137
+ if conditions.nil?
138
+ conditions = ""
139
+ else
140
+ conditions += " AND "
141
+ end
142
+ conditions += "#{klass.table_name}.#{klass.inheritance_column} = '#{klass.name.to_s}' "
143
+ end
144
+
145
+
146
+ condition_strings = Array(conditions).map do |condition|
128
147
  "(#{condition})"
129
148
  end
130
149
 
@@ -132,7 +151,7 @@ module Ultrasphinx
132
151
  "(#{klass.table_name}.#{klass.primary_key} * #{MODEL_CONFIGURATION.size} + #{class_id}) AS id",
133
152
  "#{class_id} AS class_id", "'#{klass.name}' AS class"]
134
153
  remaining_columns = fields.types.keys - ["class", "class_id"]
135
- [column_strings, [], condition_strings, [], false, remaining_columns]
154
+ [column_strings, [], condition_strings, [], false, remaining_columns, order]
136
155
  end
137
156
 
138
157
 
@@ -154,9 +173,9 @@ module Ultrasphinx
154
173
 
155
174
  def build_source(index, fields, model, options, class_id, klass, source, groups)
156
175
 
157
- column_strings, join_strings, condition_strings, group_bys, use_distinct, remaining_columns =
176
+ column_strings, join_strings, condition_strings, group_bys, use_distinct, remaining_columns, order =
158
177
  setup_source_arrays(
159
- index, klass, fields, class_id, options['conditions'])
178
+ index, klass, fields, class_id, options['conditions'], options['order'])
160
179
 
161
180
  delta_condition =
162
181
  build_delta_condition(
@@ -182,18 +201,18 @@ module Ultrasphinx
182
201
  SOURCE_SETTINGS._to_conf_string,
183
202
  setup_source_database(klass),
184
203
  range_select_string(klass, delta_condition),
185
- build_query(klass, column_strings, join_strings, condition_strings, use_distinct, group_bys),
204
+ build_query(klass, column_strings, join_strings, condition_strings, use_distinct, group_bys, order),
186
205
  "\n" + groups,
187
206
  query_info_string(klass, class_id),
188
207
  "}\n\n"]
189
208
  end
190
209
 
191
210
 
192
- def build_query(klass, column_strings, join_strings, condition_strings, use_distinct, group_bys)
211
+ def build_query(klass, column_strings, join_strings, condition_strings, use_distinct, group_bys, order)
193
212
 
194
213
  primary_key = "#{klass.table_name}.#{klass.primary_key}"
195
214
  group_bys = case ADAPTER
196
- when 'mysql'
215
+ when 'mysql', 'jdbcmysql'
197
216
  primary_key
198
217
  when 'postgresql'
199
218
  # Postgres is very fussy about GROUP_BY
@@ -211,7 +230,8 @@ module Ultrasphinx
211
230
  join_strings.uniq,
212
231
  "WHERE #{primary_key} >= $start AND #{primary_key} <= $end",
213
232
  condition_strings.uniq.map {|condition| "AND #{condition}" },
214
- "GROUP BY #{group_bys}"
233
+ "GROUP BY #{group_bys}",
234
+ ("ORDER BY #{order}" if order)
215
235
  ].flatten.compact.join(" ")
216
236
  end
217
237
 
@@ -225,10 +245,10 @@ module Ultrasphinx
225
245
 
226
246
 
227
247
  def build_regular_fields(klass, fields, entries, column_strings, join_strings, group_bys, remaining_columns)
228
- entries.to_a.each do |entry|
229
- source_string = "#{entry['table_alias']}.#{entry['field']}"
248
+ entries.to_a.each do |entry|
249
+ source_string = "#{entry['table_alias']}.#{entry['field']}"
230
250
  group_bys << source_string
231
- column_strings, remaining_columns = install_field(fields, source_string, entry['as'], entry['function_sql'], entry['facet'], column_strings, remaining_columns)
251
+ column_strings, remaining_columns = install_field(fields, source_string, entry['as'], entry['function_sql'], entry['facet'], entry['sortable'], column_strings, remaining_columns)
232
252
  end
233
253
 
234
254
  [column_strings, join_strings, group_bys, remaining_columns]
@@ -242,7 +262,7 @@ module Ultrasphinx
242
262
  association = get_association(klass, entry)
243
263
 
244
264
  # You can use 'class_name' and 'association_sql' to associate to a model that doesn't actually
245
- # have an association
265
+ # have an association.
246
266
  join_klass = association ? association.class_name.constantize : entry['class_name'].constantize
247
267
 
248
268
  raise ConfigurationError, "Unknown association from #{klass} to #{entry['class_name'] || entry['association_name']}" if not association and not entry['association_sql']
@@ -259,8 +279,8 @@ module Ultrasphinx
259
279
  end
260
280
 
261
281
  source_string = "#{entry['table_alias']}.#{entry['field']}"
262
- group_bys << source_string
263
- column_strings, remaining_columns = install_field(fields, source_string, entry['as'], entry['function_sql'], entry['facet'], column_strings, remaining_columns)
282
+ group_bys << source_string unless entry['dont_group_by']
283
+ column_strings, remaining_columns = install_field(fields, source_string, entry['as'], entry['function_sql'], entry['facet'], entry['sortable'], column_strings, remaining_columns)
264
284
  end
265
285
 
266
286
  [column_strings, join_strings, group_bys, remaining_columns]
@@ -272,26 +292,28 @@ module Ultrasphinx
272
292
  if entry['field']
273
293
  # Group concats
274
294
 
275
- # Only has_many's or explicit sql right now
295
+ # Only has_many's or explicit sql right now.
276
296
  association = get_association(klass, entry)
277
297
 
278
298
  # You can use 'class_name' and 'association_sql' to associate to a model that doesn't actually
279
- # have an association
299
+ # have an association. The automatic choice of a table alias chosen might be kind of strange.
280
300
  join_klass = association ? association.class_name.constantize : entry['class_name'].constantize
281
301
 
282
302
  join_strings = install_join_unless_association_sql(entry['association_sql'], nil, join_strings) do
283
- # XXX make sure foreign key is right for polymorphic relationships
303
+ # XXX The foreign key is not verified for polymorphic relationships.
284
304
  association = get_association(klass, entry)
285
305
  "LEFT OUTER JOIN #{join_klass.table_name} AS #{entry['table_alias']} ON #{klass.table_name}.#{klass.primary_key} = #{entry['table_alias']}.#{association.primary_key_name}" +
286
- (entry['conditions'] ? " AND (#{entry['conditions']})" : "")
306
+ # XXX Is this valid?
307
+ (entry['conditions'] ? " AND (#{entry['conditions']})" : "")
287
308
  end
288
309
 
289
310
  source_string = "#{entry['table_alias']}.#{entry['field']}"
311
+ order_string = ("ORDER BY #{entry['order']}" if entry['order'])
290
312
  # We are using the field in an aggregate, so we don't want to add it to group_bys
291
- source_string = SQL_FUNCTIONS[ADAPTER]['group_concat']._interpolate(source_string)
313
+ source_string = SQL_FUNCTIONS[ADAPTER]['group_concat']._interpolate(source_string, order_string)
292
314
  use_distinct = true
293
315
 
294
- column_strings, remaining_columns = install_field(fields, source_string, entry['as'], entry['function_sql'], entry['facet'], column_strings, remaining_columns)
316
+ column_strings, remaining_columns = install_field(fields, source_string, entry['as'], entry['function_sql'], entry['facet'], entry['sortable'], column_strings, remaining_columns)
295
317
 
296
318
  elsif entry['fields']
297
319
  # Regular concats
@@ -301,7 +323,7 @@ module Ultrasphinx
301
323
  group_bys << subsource_string
302
324
  end.join(', ') + ")"
303
325
 
304
- column_strings, remaining_columns = install_field(fields, source_string, entry['as'], entry['function_sql'], entry['facet'], column_strings, remaining_columns)
326
+ column_strings, remaining_columns = install_field(fields, source_string, entry['as'], entry['function_sql'], entry['facet'], entry['sortable'], column_strings, remaining_columns)
305
327
 
306
328
  else
307
329
  raise ConfigurationError, "Invalid concatenate parameters for #{model}: #{entry.inspect}."
@@ -311,7 +333,7 @@ module Ultrasphinx
311
333
  [column_strings, join_strings, group_bys, use_distinct, remaining_columns]
312
334
  end
313
335
 
314
-
336
+
315
337
  def build_index(index, sources)
316
338
  ["\n# Index configuration\n\n",
317
339
  "index #{index}\n{",
@@ -323,12 +345,18 @@ module Ultrasphinx
323
345
  end
324
346
 
325
347
 
326
- def install_field(fields, source_string, as, function_sql, with_facet, column_strings, remaining_columns)
348
+ def install_field(fields, source_string, as, function_sql, with_facet, with_sortable, column_strings, remaining_columns)
327
349
  source_string = function_sql._interpolate(source_string) if function_sql
328
350
 
329
351
  column_strings << fields.cast(source_string, as)
330
352
  remaining_columns.delete(as)
331
353
 
354
+ # Generate duplicate text fields for sorting
355
+ if with_sortable
356
+ column_strings << fields.cast(source_string, "#{as}_sortable")
357
+ remaining_columns.delete("#{as}_sortable")
358
+ end
359
+
332
360
  # Generate hashed integer fields for text grouping
333
361
  if with_facet
334
362
  column_strings << "#{SQL_FUNCTIONS[ADAPTER]['hash']._interpolate(source_string)} AS #{as}_facet"