ultrasphinx 1.5.3 → 1.6
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.tar.gz.sig +0 -0
- data/CHANGELOG +7 -1
- data/Manifest +6 -6
- data/README +3 -3
- data/TODO +2 -1
- data/examples/default.base +1 -0
- data/lib/ultrasphinx.rb +2 -1
- data/lib/ultrasphinx/configure.rb +10 -11
- data/lib/ultrasphinx/core_extensions.rb +2 -1
- data/lib/ultrasphinx/fields.rb +29 -18
- data/lib/ultrasphinx/search.rb +77 -92
- data/lib/ultrasphinx/search/internals.rb +133 -102
- data/lib/ultrasphinx/ultrasphinx.rb +0 -8
- data/test/integration/app/app/models/geo/state.rb +2 -1
- data/test/integration/app/app/models/person/user.rb +2 -1
- data/test/integration/app/config/environment.rb +0 -2
- data/test/integration/app/config/ultrasphinx/development.conf +6 -6
- data/test/integration/app/config/ultrasphinx/development.conf.canonical +6 -6
- data/test/integration/app/test/fixtures/sellers.yml +2 -2
- data/test/integration/app/test/fixtures/users.yml +2 -2
- data/test/integration/search_test.rb +67 -40
- data/test/setup.rb +3 -0
- data/ultrasphinx.gemspec +18 -17
- data/vendor/riddle/MIT-LICENSE +20 -0
- data/vendor/riddle/riddle.rb +15 -0
- data/vendor/riddle/riddle/client.rb +409 -0
- data/vendor/riddle/riddle/client/filter.rb +42 -0
- data/vendor/riddle/riddle/client/message.rb +54 -0
- data/vendor/riddle/riddle/client/response.rb +67 -0
- metadata +22 -16
- metadata.gz.sig +0 -0
- data/test/ts.multi +0 -2
- data/vendor/sphinx/LICENSE +0 -58
- data/vendor/sphinx/README +0 -40
- data/vendor/sphinx/Rakefile +0 -21
- data/vendor/sphinx/init.rb +0 -1
- data/vendor/sphinx/lib/client.rb +0 -647
@@ -5,7 +5,7 @@
|
|
5
5
|
crypted_password: "2fdefe5c83d80a03a828dd65e90cfff65f0fb42d043a254ca2cad6af968d0e15" #password
|
6
6
|
email: <%= "user#{num}@test.com" %>
|
7
7
|
salt: "1000"
|
8
|
-
|
9
|
-
|
8
|
+
updated_at: <%= (time = Time.parse("Tue Oct 23 04:28:11")).to_s(:db) %>
|
9
|
+
created_at: <%= (time - 180).to_s(:db) %>
|
10
10
|
deleted: <%= num == 41 ? true : false %>
|
11
11
|
<% end %>
|
@@ -14,22 +14,42 @@ class SearchTest < Test::Unit::TestCase
|
|
14
14
|
assert_equal 20, @s.results.size
|
15
15
|
end
|
16
16
|
|
17
|
-
def
|
18
|
-
|
19
|
-
assert_raises(E) { @s.total_entries }
|
20
|
-
assert_raises(E) { @s.response }
|
21
|
-
assert_raises(E) { @s.facets }
|
22
|
-
assert_raises(E) { @s.results }
|
23
|
-
end
|
24
|
-
|
25
|
-
def test_subtotals
|
17
|
+
def test_with_subtotals_option
|
18
|
+
S.client_options['with_subtotals'] = true
|
26
19
|
@s = S.new.run
|
27
20
|
assert_equal @s.total_entries, @s.subtotals.values._sum
|
21
|
+
S.client_options['with_subtotals'] = false
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_ignore_missing_records_option
|
25
|
+
@s = S.new(:per_page => 1).run
|
26
|
+
@record = @s.first
|
27
|
+
assert_equal 1, @s.size
|
28
|
+
|
29
|
+
@record.destroy
|
30
|
+
|
31
|
+
assert_raises(ActiveRecord::RecordNotFound) do
|
32
|
+
@s = S.new(:per_page => 1).run
|
33
|
+
end
|
34
|
+
|
35
|
+
S.client_options['ignore_missing_records'] = true
|
36
|
+
assert_nothing_raised do
|
37
|
+
@s = S.new(:per_page => 1).run
|
38
|
+
end
|
39
|
+
assert_equal 0, @s.size
|
40
|
+
assert_equal 1, @s.per_page
|
41
|
+
|
42
|
+
S.client_options['ignore_missing_records'] = false
|
43
|
+
|
44
|
+
# Re-insert the record... ugh
|
45
|
+
@new_record = @record.class.new(@record.attributes)
|
46
|
+
@new_record.id = @record.id
|
47
|
+
@new_record.save!
|
28
48
|
end
|
29
49
|
|
30
50
|
def test_query_retries_and_fails
|
31
51
|
system("cd #{RAILS_ROOT}; rake ultrasphinx:daemon:stop &> /dev/null")
|
32
|
-
assert_raises(
|
52
|
+
assert_raises(Ultrasphinx::DaemonError) do
|
33
53
|
S.new.run
|
34
54
|
end
|
35
55
|
system("cd #{RAILS_ROOT}; rake ultrasphinx:daemon:start &> /dev/null")
|
@@ -77,11 +97,10 @@ class SearchTest < Test::Unit::TestCase
|
|
77
97
|
end
|
78
98
|
|
79
99
|
def test_sort_by_string
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
# )
|
100
|
+
assert_equal(
|
101
|
+
Seller.find(:all, :limit => 5, :order => 'mission_statement ASC').map(&:mission_statement),
|
102
|
+
S.new(:class_names => 'Seller', :sort_by => 'mission_statement', :sort_mode => 'ascending', :per_page => 5).run.map(&:mission_statement)
|
103
|
+
)
|
85
104
|
end
|
86
105
|
|
87
106
|
def test_filter
|
@@ -104,20 +123,19 @@ class SearchTest < Test::Unit::TestCase
|
|
104
123
|
end
|
105
124
|
|
106
125
|
def test_date_range_filter
|
107
|
-
# XXX some strange boundary error in this one
|
108
126
|
@first, @last = Seller.find(5).created_at, Seller.find(10).created_at
|
109
|
-
@items = Seller.find(:all, :conditions => ['created_at
|
127
|
+
@items = Seller.find(:all, :conditions => ['created_at >= ? AND created_at <= ?', @last, @first]).sort_by(&:id)
|
110
128
|
@count = @items.size
|
111
129
|
|
112
130
|
@search = S.new(:class_names => 'Seller', :filters => {'created_at' => @first..@last}).run.sort_by(&:id)
|
113
131
|
assert_equal(@count, @search.size)
|
114
|
-
assert_equal(@items.first, @search.first)
|
115
|
-
assert_equal(@items.last, @search.last)
|
132
|
+
assert_equal(@items.first.created_at, @search.first.created_at)
|
133
|
+
assert_equal(@items.last.created_at, @search.last.created_at)
|
116
134
|
|
117
135
|
assert_equal(@count,
|
118
136
|
S.new(:class_names => 'Seller', :filters => {'created_at' => @last..@first}).run.size)
|
119
137
|
assert_equal(@count,
|
120
|
-
S.new(:class_names => 'Seller', :filters => {'created_at' => @last.strftime(STRFTIME)
|
138
|
+
S.new(:class_names => 'Seller', :filters => {'created_at' => @last.strftime(STRFTIME)..@first.strftime(STRFTIME)}).run.size)
|
121
139
|
|
122
140
|
assert_raises(Ultrasphinx::UsageError) do
|
123
141
|
S.new(:class_names => 'Seller', :filters => {'created_at' => "bogus".."sugob"}).run.size
|
@@ -132,7 +150,7 @@ class SearchTest < Test::Unit::TestCase
|
|
132
150
|
end
|
133
151
|
|
134
152
|
def test_invalid_filter
|
135
|
-
assert_raises(
|
153
|
+
assert_raises(Ultrasphinx::UsageError) do
|
136
154
|
S.new(:class_names => 'Seller', :filters => {'bogus' => 17}).run
|
137
155
|
end
|
138
156
|
end
|
@@ -140,14 +158,16 @@ class SearchTest < Test::Unit::TestCase
|
|
140
158
|
def test_conditions
|
141
159
|
@deleted_count = User.count(:conditions => {:deleted => true })
|
142
160
|
assert_equal 1, @deleted_count
|
143
|
-
assert_equal User.count - @deleted_count, S.new(:
|
161
|
+
assert_equal User.count - @deleted_count, S.new(:class_names => 'User').run.total_entries
|
144
162
|
end
|
145
163
|
|
146
|
-
# def test_mismatched_facet_configuration
|
147
|
-
#
|
148
|
-
#
|
149
|
-
#
|
150
|
-
#
|
164
|
+
# def test_mismatched_facet_configuration
|
165
|
+
# # XXX Should be caught at configuration time. For now it's your own fault
|
166
|
+
# # if you do it and get confused.
|
167
|
+
# assert_raises(Ultrasphinx::ConfigurationError) do
|
168
|
+
# Ultrasphinx::Search.new(:facets => 'company_name').run
|
169
|
+
# end
|
170
|
+
# end
|
151
171
|
|
152
172
|
def test_bogus_facet_name
|
153
173
|
assert_raises(Ultrasphinx::UsageError) do
|
@@ -156,14 +176,21 @@ class SearchTest < Test::Unit::TestCase
|
|
156
176
|
end
|
157
177
|
|
158
178
|
def test_unconfigured_sortable_name
|
159
|
-
assert_raises(
|
160
|
-
S.new(:class_names => 'Seller', :sort_by => 'company_name'
|
179
|
+
assert_raises(Ultrasphinx::UsageError) do
|
180
|
+
S.new(:class_names => 'Seller', :sort_by => 'company_name', :sort_mode => 'ascending', :per_page => 5).run
|
161
181
|
end
|
162
182
|
end
|
163
183
|
|
184
|
+
def test_sorting_by_field_with_relevance_order
|
185
|
+
assert_raises(Ultrasphinx::UsageError) do
|
186
|
+
# Defaults to :sort_mode => 'relevance'
|
187
|
+
S.new(:class_names => 'Seller', :sort_by => 'created_at', :per_page => 5).run
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
164
191
|
def test_nonexistent_sortable_name
|
165
|
-
assert_raises(
|
166
|
-
S.new(:class_names => 'Seller', :sort_by => 'bogus'
|
192
|
+
assert_raises(Ultrasphinx::UsageError) do
|
193
|
+
S.new(:class_names => 'Seller', :sort_by => 'bogus', :per_page => 5).run
|
167
194
|
end
|
168
195
|
end
|
169
196
|
|
@@ -175,7 +202,7 @@ class SearchTest < Test::Unit::TestCase
|
|
175
202
|
def test_numeric_facet
|
176
203
|
@user_id_count = Seller.find(:all).map(&:user_id).uniq.size
|
177
204
|
|
178
|
-
@s = Ultrasphinx::Search.new(:
|
205
|
+
@s = Ultrasphinx::Search.new(:class_names => 'Seller', :facets => 'user_id').run
|
179
206
|
assert_equal @user_id_count, @s.facets['user_id'].size
|
180
207
|
|
181
208
|
@s = Ultrasphinx::Search.new(:facets => 'user_id').run
|
@@ -193,25 +220,25 @@ class SearchTest < Test::Unit::TestCase
|
|
193
220
|
end
|
194
221
|
|
195
222
|
def test_float_facet
|
196
|
-
@s = Ultrasphinx::Search.new(:
|
223
|
+
@s = Ultrasphinx::Search.new(:class_names => 'Seller', :facets => 'capitalization').run
|
197
224
|
@s.facets['capitalization'].keys.each do |key|
|
198
|
-
# XXX
|
225
|
+
# XXX http://www.sphinxsearch.com/forum/view.html?id=963
|
199
226
|
# assert key.is_a?(Float)
|
200
227
|
end
|
201
228
|
end
|
202
229
|
|
203
|
-
def
|
204
|
-
|
230
|
+
def test_table_aliasing_and_association_sql
|
231
|
+
assert_equal 2, Ultrasphinx::Search.new(:class_names => 'User', :query => 'company_two:replacement').run.size
|
205
232
|
end
|
206
233
|
|
207
234
|
def test_weights
|
208
|
-
@unweighted = Ultrasphinx::Search.new(:query => '
|
209
|
-
@weighted = Ultrasphinx::Search.new(:query => '
|
210
|
-
assert_not_equal @unweighted
|
235
|
+
@unweighted = Ultrasphinx::Search.new(:query => 'seller16', :per_page => 1).run.first
|
236
|
+
@weighted = Ultrasphinx::Search.new(:query => 'seller16', :weights => {'company' => 2}, :per_page => 1).run.first
|
237
|
+
assert_not_equal @unweighted, @weighted
|
211
238
|
end
|
212
239
|
|
213
240
|
def test_excerpts
|
214
|
-
@s = Ultrasphinx::Search.new(:query => 'seller10')
|
241
|
+
@s = Ultrasphinx::Search.new(:class_names => 'Seller', :query => 'seller10')
|
215
242
|
@excerpted_item = @s.excerpt.first
|
216
243
|
@item = @s.run.first
|
217
244
|
assert_not_equal @item.name, @excerpted_item.name
|
data/test/setup.rb
CHANGED
@@ -5,6 +5,9 @@ puts ["Please make sure that test/integration/app/vendor/rails is symlinked",
|
|
5
5
|
"to a Rails trunk checkout in order for the rake tasks to run properly."]
|
6
6
|
|
7
7
|
Dir.chdir "#{File.dirname(__FILE__)}/integration/app/" do
|
8
|
+
Dir.chdir "vendor/plugins" do
|
9
|
+
system("rm ultrasphinx; ln -s ../../../../../ ultrasphinx")
|
10
|
+
end
|
8
11
|
system("rake db:create")
|
9
12
|
system("rake db:migrate db:fixtures:load")
|
10
13
|
system("rake us:boot")
|
data/ultrasphinx.gemspec
CHANGED
@@ -1,21 +1,28 @@
|
|
1
1
|
|
2
|
-
# Gem::Specification for Ultrasphinx-1.
|
2
|
+
# Gem::Specification for Ultrasphinx-1.6
|
3
3
|
# Originally generated by Echoe
|
4
4
|
|
5
5
|
Gem::Specification.new do |s|
|
6
6
|
s.name = %q{ultrasphinx}
|
7
|
-
s.version = "1.
|
8
|
-
|
9
|
-
s.
|
7
|
+
s.version = "1.6"
|
8
|
+
|
9
|
+
s.specification_version = 2 if s.respond_to? :specification_version=
|
10
|
+
|
11
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
12
|
+
s.authors = [""]
|
13
|
+
s.date = %q{2007-11-14}
|
14
|
+
s.description = %q{Ruby on Rails configurator and client to the Sphinx fulltext search engine.}
|
10
15
|
s.email = %q{}
|
16
|
+
s.files = ["CHANGELOG", "examples/ap.multi", "examples/default.base", "init.rb", "lib/ultrasphinx/autoload.rb", "lib/ultrasphinx/configure.rb", "lib/ultrasphinx/core_extensions.rb", "lib/ultrasphinx/fields.rb", "lib/ultrasphinx/hex_to_int.sql", "lib/ultrasphinx/is_indexed.rb", "lib/ultrasphinx/search/internals.rb", "lib/ultrasphinx/search/parser.rb", "lib/ultrasphinx/search.rb", "lib/ultrasphinx/spell.rb", "lib/ultrasphinx/ultrasphinx.rb", "lib/ultrasphinx.rb", "LICENSE", "Manifest", "README", "tasks/ultrasphinx.rake", "test/config/ultrasphinx/test.base", "test/integration/app/app/controllers/addresses_controller.rb", "test/integration/app/app/controllers/application.rb", "test/integration/app/app/controllers/sellers_controller.rb", "test/integration/app/app/controllers/states_controller.rb", "test/integration/app/app/controllers/users_controller.rb", "test/integration/app/app/helpers/addresses_helper.rb", "test/integration/app/app/helpers/application_helper.rb", "test/integration/app/app/helpers/sellers_helper.rb", "test/integration/app/app/helpers/states_helper.rb", "test/integration/app/app/helpers/users_helper.rb", "test/integration/app/app/models/geo/address.rb", "test/integration/app/app/models/geo/state.rb", "test/integration/app/app/models/person/user.rb", "test/integration/app/app/models/seller.rb", "test/integration/app/app/views/addresses/edit.html.erb", "test/integration/app/app/views/addresses/index.html.erb", "test/integration/app/app/views/addresses/new.html.erb", "test/integration/app/app/views/addresses/show.html.erb", "test/integration/app/app/views/layouts/addresses.html.erb", "test/integration/app/app/views/layouts/sellers.html.erb", "test/integration/app/app/views/layouts/states.html.erb", "test/integration/app/app/views/layouts/users.html.erb", "test/integration/app/app/views/sellers/edit.html.erb", "test/integration/app/app/views/sellers/index.html.erb", "test/integration/app/app/views/sellers/new.html.erb", "test/integration/app/app/views/sellers/show.html.erb", "test/integration/app/app/views/states/edit.html.erb", "test/integration/app/app/views/states/index.html.erb", "test/integration/app/app/views/states/new.html.erb", "test/integration/app/app/views/states/show.html.erb", "test/integration/app/app/views/users/edit.html.erb", "test/integration/app/app/views/users/index.html.erb", "test/integration/app/app/views/users/new.html.erb", "test/integration/app/app/views/users/show.html.erb", "test/integration/app/config/boot.rb", "test/integration/app/config/database.yml", "test/integration/app/config/environment.rb", "test/integration/app/config/environments/development.rb", "test/integration/app/config/environments/production.rb", "test/integration/app/config/environments/test.rb", "test/integration/app/config/locomotive.yml", "test/integration/app/config/routes.rb", "test/integration/app/config/ultrasphinx/default.base", "test/integration/app/config/ultrasphinx/development.conf", "test/integration/app/config/ultrasphinx/development.conf.canonical", "test/integration/app/db/migrate/001_create_users.rb", "test/integration/app/db/migrate/002_create_sellers.rb", "test/integration/app/db/migrate/003_create_addresses.rb", "test/integration/app/db/migrate/004_create_states.rb", "test/integration/app/db/migrate/005_add_capitalization_to_seller.rb", "test/integration/app/db/migrate/006_add_deleted_to_user.rb", "test/integration/app/db/migrate/007_add_lat_and_long_to_address.rb", "test/integration/app/db/migrate/008_add_mission_statement_to_seller.rb", "test/integration/app/db/schema.rb", "test/integration/app/doc/README_FOR_APP", "test/integration/app/public/404.html", "test/integration/app/public/500.html", "test/integration/app/public/dispatch.cgi", "test/integration/app/public/dispatch.fcgi", "test/integration/app/public/dispatch.rb", "test/integration/app/public/favicon.ico", "test/integration/app/public/images/rails.png", "test/integration/app/public/index.html", "test/integration/app/public/javascripts/application.js", "test/integration/app/public/javascripts/controls.js", "test/integration/app/public/javascripts/dragdrop.js", "test/integration/app/public/javascripts/effects.js", "test/integration/app/public/javascripts/prototype.js", "test/integration/app/public/robots.txt", "test/integration/app/public/stylesheets/scaffold.css", "test/integration/app/Rakefile", "test/integration/app/README", "test/integration/app/script/about", "test/integration/app/script/breakpointer", "test/integration/app/script/console", "test/integration/app/script/destroy", "test/integration/app/script/generate", "test/integration/app/script/performance/benchmarker", "test/integration/app/script/performance/profiler", "test/integration/app/script/plugin", "test/integration/app/script/process/inspector", "test/integration/app/script/process/reaper", "test/integration/app/script/process/spawner", "test/integration/app/script/runner", "test/integration/app/script/server", "test/integration/app/test/fixtures/addresses.yml", "test/integration/app/test/fixtures/sellers.yml", "test/integration/app/test/fixtures/states.yml", "test/integration/app/test/fixtures/users.yml", "test/integration/app/test/functional/addresses_controller_test.rb", "test/integration/app/test/functional/sellers_controller_test.rb", "test/integration/app/test/functional/states_controller_test.rb", "test/integration/app/test/functional/users_controller_test.rb", "test/integration/app/test/test_helper.rb", "test/integration/app/test/unit/address_test.rb", "test/integration/app/test/unit/seller_test.rb", "test/integration/app/test/unit/state_test.rb", "test/integration/app/test/unit/user_test.rb", "test/integration/configure_test.rb", "test/integration/search_test.rb", "test/integration/server_test.rb", "test/integration/spell_test.rb", "test/setup.rb", "test/test_all.rb", "test/test_helper.rb", "test/unit/parser_test.rb", "TODO", "vendor/riddle/MIT-LICENSE", "vendor/riddle/riddle/client/filter.rb", "vendor/riddle/riddle/client/message.rb", "vendor/riddle/riddle/client/response.rb", "vendor/riddle/riddle/client.rb", "vendor/riddle/riddle.rb", "vendor/will_paginate/LICENSE", "ultrasphinx.gemspec"]
|
17
|
+
s.has_rdoc = true
|
11
18
|
s.homepage = %q{http://blog.evanweaver.com/files/doc/fauna/ultrasphinx/}
|
19
|
+
s.require_paths = ["lib"]
|
12
20
|
s.rubyforge_project = %q{fauna}
|
13
|
-
s.
|
14
|
-
s.
|
15
|
-
s.authors = [""]
|
16
|
-
s.files = ["CHANGELOG", "examples/ap.multi", "examples/default.base", "init.rb", "lib/ultrasphinx/autoload.rb", "lib/ultrasphinx/configure.rb", "lib/ultrasphinx/core_extensions.rb", "lib/ultrasphinx/fields.rb", "lib/ultrasphinx/hex_to_int.sql", "lib/ultrasphinx/is_indexed.rb", "lib/ultrasphinx/search/internals.rb", "lib/ultrasphinx/search/parser.rb", "lib/ultrasphinx/search.rb", "lib/ultrasphinx/spell.rb", "lib/ultrasphinx/ultrasphinx.rb", "lib/ultrasphinx.rb", "LICENSE", "Manifest", "README", "tasks/ultrasphinx.rake", "test/config/ultrasphinx/test.base", "test/integration/app/app/controllers/addresses_controller.rb", "test/integration/app/app/controllers/application.rb", "test/integration/app/app/controllers/sellers_controller.rb", "test/integration/app/app/controllers/states_controller.rb", "test/integration/app/app/controllers/users_controller.rb", "test/integration/app/app/helpers/addresses_helper.rb", "test/integration/app/app/helpers/application_helper.rb", "test/integration/app/app/helpers/sellers_helper.rb", "test/integration/app/app/helpers/states_helper.rb", "test/integration/app/app/helpers/users_helper.rb", "test/integration/app/app/models/geo/address.rb", "test/integration/app/app/models/geo/state.rb", "test/integration/app/app/models/person/user.rb", "test/integration/app/app/models/seller.rb", "test/integration/app/app/views/addresses/edit.html.erb", "test/integration/app/app/views/addresses/index.html.erb", "test/integration/app/app/views/addresses/new.html.erb", "test/integration/app/app/views/addresses/show.html.erb", "test/integration/app/app/views/layouts/addresses.html.erb", "test/integration/app/app/views/layouts/sellers.html.erb", "test/integration/app/app/views/layouts/states.html.erb", "test/integration/app/app/views/layouts/users.html.erb", "test/integration/app/app/views/sellers/edit.html.erb", "test/integration/app/app/views/sellers/index.html.erb", "test/integration/app/app/views/sellers/new.html.erb", "test/integration/app/app/views/sellers/show.html.erb", "test/integration/app/app/views/states/edit.html.erb", "test/integration/app/app/views/states/index.html.erb", "test/integration/app/app/views/states/new.html.erb", "test/integration/app/app/views/states/show.html.erb", "test/integration/app/app/views/users/edit.html.erb", "test/integration/app/app/views/users/index.html.erb", "test/integration/app/app/views/users/new.html.erb", "test/integration/app/app/views/users/show.html.erb", "test/integration/app/config/boot.rb", "test/integration/app/config/database.yml", "test/integration/app/config/environment.rb", "test/integration/app/config/environments/development.rb", "test/integration/app/config/environments/production.rb", "test/integration/app/config/environments/test.rb", "test/integration/app/config/locomotive.yml", "test/integration/app/config/routes.rb", "test/integration/app/config/ultrasphinx/default.base", "test/integration/app/config/ultrasphinx/development.conf", "test/integration/app/config/ultrasphinx/development.conf.canonical", "test/integration/app/db/migrate/001_create_users.rb", "test/integration/app/db/migrate/002_create_sellers.rb", "test/integration/app/db/migrate/003_create_addresses.rb", "test/integration/app/db/migrate/004_create_states.rb", "test/integration/app/db/migrate/005_add_capitalization_to_seller.rb", "test/integration/app/db/migrate/006_add_deleted_to_user.rb", "test/integration/app/db/migrate/007_add_lat_and_long_to_address.rb", "test/integration/app/db/migrate/008_add_mission_statement_to_seller.rb", "test/integration/app/db/schema.rb", "test/integration/app/doc/README_FOR_APP", "test/integration/app/public/404.html", "test/integration/app/public/500.html", "test/integration/app/public/dispatch.cgi", "test/integration/app/public/dispatch.fcgi", "test/integration/app/public/dispatch.rb", "test/integration/app/public/favicon.ico", "test/integration/app/public/images/rails.png", "test/integration/app/public/index.html", "test/integration/app/public/javascripts/application.js", "test/integration/app/public/javascripts/controls.js", "test/integration/app/public/javascripts/dragdrop.js", "test/integration/app/public/javascripts/effects.js", "test/integration/app/public/javascripts/prototype.js", "test/integration/app/public/robots.txt", "test/integration/app/public/stylesheets/scaffold.css", "test/integration/app/Rakefile", "test/integration/app/README", "test/integration/app/script/about", "test/integration/app/script/breakpointer", "test/integration/app/script/console", "test/integration/app/script/destroy", "test/integration/app/script/generate", "test/integration/app/script/performance/benchmarker", "test/integration/app/script/performance/profiler", "test/integration/app/script/plugin", "test/integration/app/script/process/inspector", "test/integration/app/script/process/reaper", "test/integration/app/script/process/spawner", "test/integration/app/script/runner", "test/integration/app/script/server", "test/integration/app/test/fixtures/addresses.yml", "test/integration/app/test/fixtures/sellers.yml", "test/integration/app/test/fixtures/states.yml", "test/integration/app/test/fixtures/users.yml", "test/integration/app/test/functional/addresses_controller_test.rb", "test/integration/app/test/functional/sellers_controller_test.rb", "test/integration/app/test/functional/states_controller_test.rb", "test/integration/app/test/functional/users_controller_test.rb", "test/integration/app/test/test_helper.rb", "test/integration/app/test/unit/address_test.rb", "test/integration/app/test/unit/seller_test.rb", "test/integration/app/test/unit/state_test.rb", "test/integration/app/test/unit/user_test.rb", "test/integration/configure_test.rb", "test/integration/search_test.rb", "test/integration/server_test.rb", "test/integration/spell_test.rb", "test/setup.rb", "test/test_all.rb", "test/test_helper.rb", "test/ts.multi", "test/unit/parser_test.rb", "TODO", "vendor/sphinx/init.rb", "vendor/sphinx/lib/client.rb", "vendor/sphinx/LICENSE", "vendor/sphinx/Rakefile", "vendor/sphinx/README", "vendor/will_paginate/LICENSE", "ultrasphinx.gemspec"]
|
21
|
+
s.rubygems_version = %q{0.9.4.6}
|
22
|
+
s.summary = %q{Ruby on Rails configurator and client to the Sphinx fulltext search engine.}
|
17
23
|
s.test_files = ["test/test_all.rb"]
|
18
|
-
|
24
|
+
|
25
|
+
s.add_dependency(%q<chronic>, [">= 0"])
|
19
26
|
end
|
20
27
|
|
21
28
|
|
@@ -31,12 +38,6 @@ end
|
|
31
38
|
# p.docs_host = "blog.evanweaver.com:~/www/bax/public/files/doc/"
|
32
39
|
# p.rdoc_pattern = /is_indexed.rb|search.rb|spell.rb|ultrasphinx.rb|^README|TODO|CHANGELOG|^LICENSE/
|
33
40
|
# p.dependencies = "chronic"
|
34
|
-
#
|
35
|
-
#
|
36
|
-
# require 'rcov/rcovtask'
|
37
|
-
#
|
38
|
-
# Rcov::RcovTask.new(:coverage) do |t|
|
39
|
-
# t.test_files = FileList['test/integration/*.rb', 'test/unit/*.rb']
|
40
|
-
# t.rcov_opts << '--include-file test\/integration\/app\/vendor\/plugins\/ultrasphinx\/lib\/.*\.rb'
|
41
|
-
# t.verbose = true
|
41
|
+
# p.test_pattern = ["test/integration/*.rb", "test/unit/*.rb"]
|
42
|
+
# p.rcov_options << '--include-file test\/integration\/app\/vendor\/plugins\/ultrasphinx\/lib\/.*\.rb'
|
42
43
|
# end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2007 Pat Allan
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'riddle/client'
|
2
|
+
require 'riddle/client/filter'
|
3
|
+
require 'riddle/client/message'
|
4
|
+
require 'riddle/client/response'
|
5
|
+
|
6
|
+
module Riddle #:nodoc:
|
7
|
+
module Version #:nodoc:
|
8
|
+
Major = 0
|
9
|
+
Minor = 9
|
10
|
+
Tiny = 8
|
11
|
+
Rev = 871
|
12
|
+
|
13
|
+
String = [Major, Minor, Tiny].join('.') + "r#{Rev}"
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,409 @@
|
|
1
|
+
module Riddle
|
2
|
+
class VersionError < StandardError; end
|
3
|
+
class ResponseError < StandardError; end
|
4
|
+
|
5
|
+
# This class was heavily based on the existing Client API by Dmytro Shteflyuk
|
6
|
+
# and Alexy Kovyrin. Their code worked fine, I just wanted something a bit
|
7
|
+
# more Ruby-ish (ie. lowercase and underscored method names). I also have
|
8
|
+
# used a few helper classes, just to neaten things up.
|
9
|
+
#
|
10
|
+
# I do plan to release this part of the plugin as a standalone library at
|
11
|
+
# some point - if you're interested in using it, and are feeling impatient,
|
12
|
+
# feel free to hassle me.
|
13
|
+
#
|
14
|
+
class Client
|
15
|
+
Commands = {
|
16
|
+
:search => 0, # SEARCHD_COMMAND_SEARCH
|
17
|
+
:excerpt => 1, # SEARCHD_COMMAND_EXCERPT
|
18
|
+
:update => 2 # SEARCHD_COMMAND_UPDATE
|
19
|
+
}
|
20
|
+
|
21
|
+
Versions = {
|
22
|
+
:search => 0x10F, # VER_COMMAND_SEARCH
|
23
|
+
:excerpt => 0x100, # VER_COMMAND_EXCERPT
|
24
|
+
:update => 0x100 # VER_COMMAND_UPDATE
|
25
|
+
}
|
26
|
+
|
27
|
+
Statuses = {
|
28
|
+
:ok => 0, # SEARCHD_OK
|
29
|
+
:error => 1, # SEARCHD_ERROR
|
30
|
+
:retry => 2, # SEARCHD_RETRY
|
31
|
+
:warning => 3 # SEARCHD_WARNING
|
32
|
+
}
|
33
|
+
|
34
|
+
MatchModes = {
|
35
|
+
:all => 0, # SPH_MATCH_ALL
|
36
|
+
:any => 1, # SPH_MATCH_ANY
|
37
|
+
:phrase => 2, # SPH_MATCH_PHRASE
|
38
|
+
:boolean => 3, # SPH_MATCH_BOOLEAN
|
39
|
+
:extended => 4 # SPH_MATCH_EXTENDED
|
40
|
+
}
|
41
|
+
|
42
|
+
SortModes = {
|
43
|
+
:relevance => 0, # SPH_SORT_RELEVANCE
|
44
|
+
:attr_desc => 1, # SPH_SORT_ATTR_DESC
|
45
|
+
:attr_asc => 2, # SPH_SORT_ATTR_ASC
|
46
|
+
:time_segments => 3, # SPH_SORT_TIME_SEGMENTS
|
47
|
+
:extended => 4 # SPH_SORT_EXTENDED
|
48
|
+
}
|
49
|
+
|
50
|
+
AttributeTypes = {
|
51
|
+
:integer => 1, # SPH_ATTR_INTEGER
|
52
|
+
:timestamp => 2, # SPH_ATTR_TIMESTAMP
|
53
|
+
:ordinal => 3, # SPH_ATTR_ORDINAL
|
54
|
+
:bool => 4, # SPH_ATTR_BOOL
|
55
|
+
:float => 5, # SPH_ATTR_FLOAT
|
56
|
+
:multi => 0x40000000 # SPH_ATTR_MULTI
|
57
|
+
}
|
58
|
+
|
59
|
+
GroupFunctions = {
|
60
|
+
:day => 0, # SPH_GROUPBY_DAY
|
61
|
+
:week => 1, # SPH_GROUPBY_WEEK
|
62
|
+
:month => 2, # SPH_GROUPBY_MONTH
|
63
|
+
:year => 3, # SPH_GROUPBY_YEAR
|
64
|
+
:attr => 4, # SPH_GROUPBY_ATTR
|
65
|
+
:attrpair => 5 # SPH_GROUPBY_ATTRPAIR
|
66
|
+
}
|
67
|
+
|
68
|
+
FilterTypes = {
|
69
|
+
:values => 0, # SPH_FILTER_VALUES
|
70
|
+
:range => 1, # SPH_FILTER_RANGE
|
71
|
+
:float_range => 2 # SPH_FILTER_FLOATRANGE
|
72
|
+
}
|
73
|
+
|
74
|
+
attr_accessor :server, :port, :offset, :limit, :max_matches,
|
75
|
+
:match_mode, :sort_mode, :sort_by, :weights, :id_range, :filters,
|
76
|
+
:group_by, :group_function, :group_clause, :group_distinct, :cut_off,
|
77
|
+
:retry_count, :retry_delay, :anchor
|
78
|
+
attr_reader :queue
|
79
|
+
|
80
|
+
# Can instantiate with a specific server and port - otherwise it assumes
|
81
|
+
# defaults of localhost and 3312 respectively. All other settings can be
|
82
|
+
# accessed and changed via the attribute accessors.
|
83
|
+
def initialize(server=nil, port=nil)
|
84
|
+
@server = server || "localhost"
|
85
|
+
@port = port || 3312
|
86
|
+
|
87
|
+
# defaults
|
88
|
+
@offset = 0
|
89
|
+
@limit = 20
|
90
|
+
@max_matches = 1000
|
91
|
+
@match_mode = :all
|
92
|
+
@sort_mode = :relevance
|
93
|
+
@sort_by = ''
|
94
|
+
@weights = []
|
95
|
+
@id_range = 0..0
|
96
|
+
@filters = []
|
97
|
+
@group_by = ''
|
98
|
+
@group_function = :day
|
99
|
+
@group_clause = '@group desc'
|
100
|
+
@group_distinct = ''
|
101
|
+
@cut_off = 0
|
102
|
+
@retry_count = 0
|
103
|
+
@retry_delay = 0
|
104
|
+
@anchor = {}
|
105
|
+
# string keys are index names, integer values are weightings
|
106
|
+
@index_weights = {}
|
107
|
+
|
108
|
+
@queue = []
|
109
|
+
end
|
110
|
+
|
111
|
+
def set_anchor(lat_attr, lat, long_attr, long)
|
112
|
+
@anchor = {
|
113
|
+
:latitude_attribute => lat_attr,
|
114
|
+
:latitude => lat,
|
115
|
+
:longtitude_attribute => long_attr,
|
116
|
+
:longtitude => long
|
117
|
+
}
|
118
|
+
end
|
119
|
+
|
120
|
+
def append_query(search, index = '*')
|
121
|
+
@queue << query_message(search, index)
|
122
|
+
end
|
123
|
+
|
124
|
+
def run
|
125
|
+
response = Response.new request(:search, @queue)
|
126
|
+
|
127
|
+
results = @queue.collect do
|
128
|
+
result = {
|
129
|
+
:matches => [],
|
130
|
+
:fields => [],
|
131
|
+
:attributes => {},
|
132
|
+
:attribute_names => [],
|
133
|
+
:words => {}
|
134
|
+
}
|
135
|
+
|
136
|
+
result[:status] = response.next_int
|
137
|
+
case result[:status]
|
138
|
+
when Statuses[:warning]
|
139
|
+
result[:warning] = response.next
|
140
|
+
when Statuses[:error]
|
141
|
+
result[:error] = response.next
|
142
|
+
next result
|
143
|
+
end
|
144
|
+
|
145
|
+
result[:fields] = response.next_array
|
146
|
+
|
147
|
+
attributes = response.next_int
|
148
|
+
for i in 0...attributes
|
149
|
+
attribute_name = response.next
|
150
|
+
type = response.next_int
|
151
|
+
|
152
|
+
result[:attributes][attribute_name] = type
|
153
|
+
result[:attribute_names] << attribute_name
|
154
|
+
end
|
155
|
+
|
156
|
+
matches = response.next_int
|
157
|
+
is_64_bit = response.next_int
|
158
|
+
for i in 0...matches
|
159
|
+
doc = is_64_bit > 0 ? (response.next_int() << 32) + response.next_int : response.next_int
|
160
|
+
weight = response.next_int
|
161
|
+
|
162
|
+
result[:matches] << {:doc => doc, :weight => weight, :index => i, :attributes => {}}
|
163
|
+
result[:attribute_names].each do |attr|
|
164
|
+
case result[:attributes][attr]
|
165
|
+
when AttributeTypes[:float]
|
166
|
+
result[:matches].last[:attributes][attr] = response.next_float
|
167
|
+
when AttributeTypes[:multi]
|
168
|
+
result[:matches].last[:attributes][attr] = response.next_int_array
|
169
|
+
else
|
170
|
+
result[:matches].last[:attributes][attr] = response.next_int
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
result[:total] = response.next_int.to_i || 0
|
176
|
+
result[:total_found] = response.next_int.to_i || 0
|
177
|
+
result[:time] = ('%.3f' % (response.next_int / 1000.0)).to_f || 0.0
|
178
|
+
|
179
|
+
words = response.next_int
|
180
|
+
for i in 0...words
|
181
|
+
word = response.next
|
182
|
+
docs = response.next_int
|
183
|
+
hits = response.next_int
|
184
|
+
result[:words][word] = {:docs => docs, :hits => hits}
|
185
|
+
end
|
186
|
+
|
187
|
+
result
|
188
|
+
end
|
189
|
+
|
190
|
+
@queue.clear
|
191
|
+
results
|
192
|
+
end
|
193
|
+
|
194
|
+
# Query the Sphinx daemon - defaulting to all indexes, but you can specify
|
195
|
+
# a specific one if you wish. The search parameter should be a string
|
196
|
+
# following Sphinx's expectations.
|
197
|
+
def query(search, index = '*')
|
198
|
+
@queue << query_message(search, index)
|
199
|
+
self.run.first
|
200
|
+
end
|
201
|
+
|
202
|
+
# Grab excerpts from the indexes. As part of the options, you will need to
|
203
|
+
# define:
|
204
|
+
# * :docs
|
205
|
+
# * :words
|
206
|
+
# * :index
|
207
|
+
#
|
208
|
+
# Optional settings include:
|
209
|
+
# * :before_match (defaults to <span class="match">)
|
210
|
+
# * :after_match (defaults to </span>)
|
211
|
+
# * :chunk_separator (defaults to ' … ' - which is an HTML ellipsis)
|
212
|
+
# * :limit (defaults to 256)
|
213
|
+
# * :around (defaults to 5)
|
214
|
+
#
|
215
|
+
# The defaults differ from the official PHP client, as I've opted for
|
216
|
+
# semantic HTML markup.
|
217
|
+
def excerpts(options = {})
|
218
|
+
options[:index] ||= '*'
|
219
|
+
options[:before_match] ||= '<span class="match">'
|
220
|
+
options[:after_match] ||= '</span>'
|
221
|
+
options[:chunk_separator] ||= ' … ' # ellipsis
|
222
|
+
options[:limit] ||= 256
|
223
|
+
options[:around] ||= 5
|
224
|
+
|
225
|
+
response = Response.new request(:excerpt, excerpts_message(options))
|
226
|
+
|
227
|
+
options[:docs].collect { response.next }
|
228
|
+
end
|
229
|
+
|
230
|
+
# Update attributes
|
231
|
+
def update(index, attributes, values_by_doc)
|
232
|
+
response = Response.new request(
|
233
|
+
:update,
|
234
|
+
update_message(index, attributes, values_by_doc)
|
235
|
+
)
|
236
|
+
|
237
|
+
response.next_int
|
238
|
+
end
|
239
|
+
|
240
|
+
private
|
241
|
+
|
242
|
+
# Connects to the Sphinx daemon, and yields a socket to use. The socket is
|
243
|
+
# closed at the end of the block.
|
244
|
+
def connect(&block)
|
245
|
+
socket = TCPSocket.new @server, @port
|
246
|
+
|
247
|
+
# Checking version
|
248
|
+
version = socket.recv(4).unpack('N*').first
|
249
|
+
if version < 1
|
250
|
+
socket.close
|
251
|
+
raise VersionError, "Can only connect to searchd version 1.0 or better, not version #{version}"
|
252
|
+
end
|
253
|
+
|
254
|
+
# Send version
|
255
|
+
socket.send [1].pack('N'), 0
|
256
|
+
|
257
|
+
begin
|
258
|
+
yield socket
|
259
|
+
ensure
|
260
|
+
socket.close
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
# Send a collection of messages, for a command type (eg, search, excerpts,
|
265
|
+
# update), to the Sphinx daemon.
|
266
|
+
def request(command, messages)
|
267
|
+
response = ""
|
268
|
+
status = -1
|
269
|
+
version = 0
|
270
|
+
length = 0
|
271
|
+
message = Array(messages).join("")
|
272
|
+
|
273
|
+
connect do |socket|
|
274
|
+
case command
|
275
|
+
when :search
|
276
|
+
# Message length is +4 to account for the following count value for
|
277
|
+
# the number of messages (well, that's what I'm assuming).
|
278
|
+
socket.send [
|
279
|
+
Commands[command], Versions[command],
|
280
|
+
4+message.length, messages.length
|
281
|
+
].pack("nnNN") + message, 0
|
282
|
+
else
|
283
|
+
socket.send [
|
284
|
+
Commands[command], Versions[command], message.length
|
285
|
+
].pack("nnN") + message, 0
|
286
|
+
end
|
287
|
+
|
288
|
+
header = socket.recv(8)
|
289
|
+
status, version, length = header.unpack('n2N')
|
290
|
+
|
291
|
+
while response.length < length
|
292
|
+
part = socket.recv(length - response.length)
|
293
|
+
response << part if part
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
if response.empty? || response.length != length
|
298
|
+
raise ResponseError, "No response from searchd (status: #{status}, version: #{version})"
|
299
|
+
end
|
300
|
+
|
301
|
+
case status
|
302
|
+
when Statuses[:ok]
|
303
|
+
if version < Versions[command]
|
304
|
+
puts format("searchd command v.%d.%d older than client (v.%d.%d)",
|
305
|
+
version >> 8, version & 0xff,
|
306
|
+
Versions[command] >> 8, Versions[command] & 0xff)
|
307
|
+
end
|
308
|
+
response
|
309
|
+
when Statuses[:warning]
|
310
|
+
length = response[0, 4].unpack('N*').first
|
311
|
+
puts response[4, length]
|
312
|
+
response[4 + length, response.length - 4 - length]
|
313
|
+
when Statuses[:error], Statuses[:retry]
|
314
|
+
raise ResponseError, "searchd error (status: #{status}): #{response[4, response.length - 4]}"
|
315
|
+
else
|
316
|
+
raise ResponseError, "Unknown searchd error (status: #{status})"
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
320
|
+
# Generation of the message to send to Sphinx for a search.
|
321
|
+
def query_message(search, index)
|
322
|
+
message = Message.new
|
323
|
+
|
324
|
+
# Mode, Limits, Sort Mode
|
325
|
+
message.append_ints @offset, @limit, MatchModes[@match_mode], SortModes[@sort_mode]
|
326
|
+
message.append_string @sort_by
|
327
|
+
|
328
|
+
# Query
|
329
|
+
message.append_string search
|
330
|
+
|
331
|
+
# Weights
|
332
|
+
message.append_int @weights.length
|
333
|
+
message.append_ints *@weights
|
334
|
+
|
335
|
+
# Index
|
336
|
+
message.append_string index
|
337
|
+
|
338
|
+
# ID Range
|
339
|
+
message.append_ints 0, @id_range.first, @id_range.last
|
340
|
+
|
341
|
+
# Filters
|
342
|
+
message.append_int @filters.length
|
343
|
+
@filters.each { |filter| message.append filter.query_message }
|
344
|
+
|
345
|
+
# Grouping
|
346
|
+
message.append_int GroupFunctions[@group_function]
|
347
|
+
message.append_string @group_by
|
348
|
+
message.append_int @max_matches
|
349
|
+
message.append_string @group_clause
|
350
|
+
message.append_ints @cut_off, @retry_count, @retry_delay
|
351
|
+
message.append_string @group_distinct
|
352
|
+
|
353
|
+
# Anchor Point
|
354
|
+
if @anchor.empty?
|
355
|
+
message.append_int 0
|
356
|
+
else
|
357
|
+
message.append_int 1
|
358
|
+
message.append_string @anchor[:latitude_attribute]
|
359
|
+
message.append_string @anchor[:longtitude_attribute]
|
360
|
+
message.append_floats @anchor[:latitude], @anchor[:longtitude]
|
361
|
+
end
|
362
|
+
|
363
|
+
# Per Index Weights
|
364
|
+
message.append_int @index_weights.length
|
365
|
+
@index_weights.each do |key,val|
|
366
|
+
message.append_string key
|
367
|
+
message.append_int val
|
368
|
+
end
|
369
|
+
|
370
|
+
message.to_s
|
371
|
+
end
|
372
|
+
|
373
|
+
# Generation of the message to send to Sphinx for an excerpts request.
|
374
|
+
def excerpts_message(options)
|
375
|
+
message = Message.new
|
376
|
+
|
377
|
+
message.append [0, 1].pack('N2') # mode = 0, flags = 1
|
378
|
+
message.append_string options[:index]
|
379
|
+
message.append_string options[:words]
|
380
|
+
|
381
|
+
# options
|
382
|
+
message.append_string options[:before_match]
|
383
|
+
message.append_string options[:after_match]
|
384
|
+
message.append_string options[:chunk_separator]
|
385
|
+
message.append_ints options[:limit], options[:around]
|
386
|
+
|
387
|
+
message.append_array options[:docs]
|
388
|
+
|
389
|
+
message.to_s
|
390
|
+
end
|
391
|
+
|
392
|
+
# Generation of the message to send to Sphinx to update attributes of a
|
393
|
+
# document.
|
394
|
+
def update_message(index, attributes, values_by_doc)
|
395
|
+
message = Message.new
|
396
|
+
|
397
|
+
message.append_string index
|
398
|
+
message.append_array attributes
|
399
|
+
|
400
|
+
message.append_int values_by_doc.length
|
401
|
+
values_by_doc.each do |key,values|
|
402
|
+
message.append_int key # document ID
|
403
|
+
message.append_ints *values # array of new values (integers)
|
404
|
+
end
|
405
|
+
|
406
|
+
message.to_s
|
407
|
+
end
|
408
|
+
end
|
409
|
+
end
|