rlibsphinxclient 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. data/.gitignore +3 -0
  2. data/CHANGELOG.rdoc +18 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +151 -0
  5. data/Rakefile +39 -0
  6. data/VERSION +1 -0
  7. data/ext/extconf.rb +20 -0
  8. data/ext/rlibsphinxclient.i +314 -0
  9. data/ext/rlibsphinxclient_wrap.c +5931 -0
  10. data/init.rb +1 -0
  11. data/lib/sphinx.rb +22 -0
  12. data/lib/sphinx/client.rb +1070 -0
  13. data/lib/sphinx/fast_client.rb +184 -0
  14. data/lib/sphinx/request.rb +49 -0
  15. data/lib/sphinx/response.rb +69 -0
  16. data/lib/sphinx/safe_executor.rb +11 -0
  17. data/lib/sphinx/timeout.rb +9 -0
  18. data/rlibsphinxclient.gemspec +117 -0
  19. data/spec/client_response_spec.rb +135 -0
  20. data/spec/client_spec.rb +548 -0
  21. data/spec/fixtures/default_search.php +8 -0
  22. data/spec/fixtures/default_search_index.php +8 -0
  23. data/spec/fixtures/excerpt_custom.php +11 -0
  24. data/spec/fixtures/excerpt_default.php +8 -0
  25. data/spec/fixtures/excerpt_flags.php +11 -0
  26. data/spec/fixtures/field_weights.php +9 -0
  27. data/spec/fixtures/filter.php +9 -0
  28. data/spec/fixtures/filter_exclude.php +9 -0
  29. data/spec/fixtures/filter_float_range.php +9 -0
  30. data/spec/fixtures/filter_float_range_exclude.php +9 -0
  31. data/spec/fixtures/filter_range.php +9 -0
  32. data/spec/fixtures/filter_range_exclude.php +9 -0
  33. data/spec/fixtures/filter_ranges.php +10 -0
  34. data/spec/fixtures/filters.php +10 -0
  35. data/spec/fixtures/filters_different.php +13 -0
  36. data/spec/fixtures/geo_anchor.php +9 -0
  37. data/spec/fixtures/group_by_attr.php +9 -0
  38. data/spec/fixtures/group_by_attrpair.php +9 -0
  39. data/spec/fixtures/group_by_day.php +9 -0
  40. data/spec/fixtures/group_by_day_sort.php +9 -0
  41. data/spec/fixtures/group_by_month.php +9 -0
  42. data/spec/fixtures/group_by_week.php +9 -0
  43. data/spec/fixtures/group_by_year.php +9 -0
  44. data/spec/fixtures/group_distinct.php +10 -0
  45. data/spec/fixtures/id_range.php +9 -0
  46. data/spec/fixtures/id_range64.php +9 -0
  47. data/spec/fixtures/index_weights.php +9 -0
  48. data/spec/fixtures/keywords.php +8 -0
  49. data/spec/fixtures/limits.php +9 -0
  50. data/spec/fixtures/limits_cutoff.php +9 -0
  51. data/spec/fixtures/limits_max.php +9 -0
  52. data/spec/fixtures/limits_max_cutoff.php +9 -0
  53. data/spec/fixtures/match_all.php +9 -0
  54. data/spec/fixtures/match_any.php +9 -0
  55. data/spec/fixtures/match_boolean.php +9 -0
  56. data/spec/fixtures/match_extended.php +9 -0
  57. data/spec/fixtures/match_extended2.php +9 -0
  58. data/spec/fixtures/match_fullscan.php +9 -0
  59. data/spec/fixtures/match_phrase.php +9 -0
  60. data/spec/fixtures/max_query_time.php +9 -0
  61. data/spec/fixtures/miltiple_queries.php +12 -0
  62. data/spec/fixtures/ranking_bm25.php +9 -0
  63. data/spec/fixtures/ranking_none.php +9 -0
  64. data/spec/fixtures/ranking_proximity_bm25.php +9 -0
  65. data/spec/fixtures/ranking_wordcount.php +9 -0
  66. data/spec/fixtures/retries.php +9 -0
  67. data/spec/fixtures/retries_delay.php +9 -0
  68. data/spec/fixtures/sort_attr_asc.php +9 -0
  69. data/spec/fixtures/sort_attr_desc.php +9 -0
  70. data/spec/fixtures/sort_expr.php +9 -0
  71. data/spec/fixtures/sort_extended.php +9 -0
  72. data/spec/fixtures/sort_relevance.php +9 -0
  73. data/spec/fixtures/sort_time_segments.php +9 -0
  74. data/spec/fixtures/sphinxapi.php +1181 -0
  75. data/spec/fixtures/update_attributes.php +8 -0
  76. data/spec/fixtures/weights.php +9 -0
  77. data/spec/sphinx/sphinx.conf +67 -0
  78. data/spec/sphinx/sphinx_test.sql +86 -0
  79. metadata +133 -0
data/.gitignore ADDED
@@ -0,0 +1,3 @@
1
+ mkmf.log
2
+ pkg/
3
+ rdoc
data/CHANGELOG.rdoc ADDED
@@ -0,0 +1,18 @@
1
+ === Version 0.2.2 / 2009-10-15
2
+
3
+ * Moved from Echoe to Jeweler, gem is hosted on Gemcutter now.
4
+
5
+ === Version 0.2.1 / 2009-01-27
6
+
7
+ * 4 minor bug fixes
8
+
9
+ * Fixed extconf.rb to use /opt/sphinx as default library search path.
10
+ * Updated Mac OS X installation steps and prerequisites section in README.rdoc.
11
+ * Updated Manifest to include LICENSE file.
12
+ * Added CHANGELOG file.
13
+
14
+ === Version 0.2.0 / 2009-01-27
15
+
16
+ This is the first preview release of rsphinxclientapi, a Ruby wrapper for pure C searchd client API library.
17
+
18
+ * Implemented wrappers for all API functions.
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 Dmytro Shteflyuk
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.
data/README.rdoc ADDED
@@ -0,0 +1,151 @@
1
+ = rlibsphinxclient
2
+
3
+ A Ruby wrapper for pure C searchd client API library. This is *highly experimental* library
4
+ so use it at your own risk.
5
+
6
+ == Installing the rlibsphinxclient gem
7
+
8
+ This gem can be more difficult to install than the typical Ruby extension. First you have to
9
+ install Sphinx and Sphinx pure C searchd client API library.
10
+
11
+ === Step 1: Install pure C Sphinx client API
12
+
13
+ Go to http://sphinxsearch.com/downloads.html and download the latest stable release.
14
+ Then go to <tt>api/libsphinxclient</tt> directory and install client API to your
15
+ preferred folder (I like /opt/sphinx):
16
+
17
+ cd api/libsphinxclient
18
+ ./configure --prefix=/opt/sphinx
19
+ make
20
+ sudo make install
21
+
22
+ On Max OS X you may get the following error:
23
+
24
+ configure: error: C++ preprocessor "/lib/cpp" fails sanity check
25
+
26
+ In this case you should specify environment variable for <tt>./configure</tt> script:
27
+
28
+ CXXCPP="gcc -E" ./configure --prefix=/opt/sphinx
29
+
30
+ === Step 2: Install rlibsphinxclient gem
31
+
32
+ If you have installed the Sphinx to <tt>/opt/sphinx</tt>, just run:
33
+
34
+ sudo gem install kpumuk-rlibsphinxclient --no-ri --no-rdoc
35
+
36
+ Otherwise, specify where sphinx has been installed to:
37
+
38
+ sudo gem install kpumuk-rlibsphinxclient --no-ri --no-rdoc -- --with-libsphinxclient-dir=/opt/sphinx-0.9.9
39
+
40
+ On Mac OS X with MacPorts you should specify <tt>ARCHFLAGS</tt> environment variable:
41
+
42
+ sudo env ARCHFLAGS="-arch i386" gem install kpumuk-rlibsphinxclient --no-rdoc --no-ri -- --with-libsphinxclient-dir=/opt/sphinx-0.9.9
43
+
44
+ If you are working on Ruby on Rails application, you can add gem dependency to your
45
+ <tt>config/environment.rb</tt>:
46
+
47
+ config.gem 'kpumuk-rlibsphinxclient', :lib => 'sphinx'
48
+
49
+ Also don't forget to remove the <tt>sphinx</tt> plugin, because it's functionality
50
+ is completely covered by this gem.
51
+
52
+ == Using the rlibsphinxclient gem
53
+
54
+ The gem includes two versions of the client API: pure Ruby and wrapper for pure C client API.
55
+ They are 100% equivalent in use, so you can switch to any of them. To use pure Ruby client,
56
+ instantiate the <tt>Sphinx::Client</tt>, for pure C wrapper use <tt>Sphinx::FastClient</tt>.
57
+
58
+ Important note: you should call <tt>destroy</tt> method when you do not need client API any more.
59
+ The reason for that is the C wrapper saves all query results in memory, and frees them in
60
+ the <tt>destroy</tt> method call. You can omit this call in pure Ruby library, but I'd like
61
+ to do call in any case just for consistence (to be able to switch to another client).
62
+
63
+ Important note #2: to ensure that <tt>destroy</tt> method will be called, use <tt>ensure</tt>
64
+ block:
65
+
66
+ begin
67
+ @sphinx = Sphinx::FastClient.new
68
+ @sphinx.Query('test')
69
+ ensure
70
+ @sphinx.destroy
71
+ end
72
+
73
+ == Examples of usage
74
+
75
+ Ok, let's take a look at the examples. First, here is the search example with all possible
76
+ filters and options set:
77
+
78
+ require 'sphinx'
79
+ @sphinx = Sphinx::FastClient.new
80
+ @sphinx.SetServer('localhost', 3312)
81
+ @sphinx.SetLimits(1, 100, 20, 30)
82
+ @sphinx.SetMaxQueryTime(5)
83
+ @sphinx.SetMatchMode(Sphinx::Client::SPH_MATCH_EXTENDED2)
84
+ @sphinx.SetRankingMode(Sphinx::Client::SPH_RANK_BM25)
85
+ @sphinx.SetSortMode(Sphinx::Client::SPH_SORT_RELEVANCE)
86
+ @sphinx.SetFieldWeights('group_id' => 10, 'rating' => 20)
87
+ @sphinx.SetIndexWeights('test1' => 20, 'test2' => 30)
88
+ @sphinx.SetIDRange(1, 100)
89
+ @sphinx.SetFilter('group_id', [1], true)
90
+ @sphinx.SetFilterRange('group_id', 1, 2, true)
91
+ @sphinx.SetFilterFloatRange('rating', 1, 3, true)
92
+ @sphinx.SetGroupBy('created_at', Sphinx::Client::SPH_GROUPBY_DAY)
93
+ @sphinx.SetGroupDistinct('group_id')
94
+ @sphinx.SetRetries(5, 10)
95
+ results = @sphinx.Query('test')
96
+ @sphinx.destroy
97
+
98
+ <tt>BuildKeywords</tt> example:
99
+
100
+ require 'sphinx'
101
+ @sphinx = Sphinx::FastClient.new
102
+ results = @sphinx.BuildKeywords('wifi gprs', 'test1', true)
103
+ @sphinx.destroy
104
+
105
+ <tt>BuildExcerpts</tt> example:
106
+
107
+ require 'sphinx'
108
+ @sphinx = Sphinx::FastClient.new
109
+ results = @sphinx.BuildExcerpts(['what the world', 'London is the capital of Great Britain'], 'test1', 'the')
110
+ @sphinx.destroy
111
+
112
+ <tt>UpdateAttributes</tt> example:
113
+
114
+ require 'sphinx'
115
+ @sphinx = Sphinx::FastClient.new
116
+ results = @sphinx.UpdateAttributes('test1', ['group_id'], { 2 => [1] })
117
+ @sphinx.destroy
118
+
119
+ == Benchmarks
120
+
121
+ The reason to write this gem was to investigate why we keep getting timeout errors
122
+ when using Sphinx (occur rarely, but they are annoying me.) But the side effect of
123
+ this library was the slight search performance improvement: Ruby library is slower
124
+ when generating Sphinx request and parsing its results.
125
+
126
+ require 'sphinx'
127
+ require 'benchmark'
128
+
129
+ def run_test(klass)
130
+ sphinx = klass.new
131
+ sphinx.Query('test hello')
132
+ ensure
133
+ sphinx.destroy
134
+ end
135
+
136
+ Benchmark.bm do |x|
137
+ x.report('pure ruby') { 1000.times { run_test(Sphinx::Client) } }
138
+ x.report('c wrapper') { 1000.times { run_test(Sphinx::FastClient) } }
139
+ end
140
+
141
+ On my MBP I got the following results:
142
+
143
+ user system total real
144
+ pure ruby 0.420000 0.230000 0.650000 ( 14.721659)
145
+ c wrapper 0.060000 0.090000 0.150000 ( 2.248645)
146
+
147
+ == Who are the authors?
148
+
149
+ This plugin has been created in Scribd.com for our internal use and then the sources were opened
150
+ for other people to use. All the code in this package has been developed by Dmytro Shteflyuk
151
+ for Scribd.com and is released under the MIT license. For more details, see MIT-LICENSE file.
data/Rakefile ADDED
@@ -0,0 +1,39 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'spec/rake/spectask'
4
+ require 'rake/rdoctask'
5
+
6
+ begin
7
+ require 'jeweler'
8
+ Jeweler::Tasks.new do |gemspec|
9
+ gemspec.name = 'rlibsphinxclient'
10
+ gemspec.summary = 'A Ruby wrapper for pure C searchd client API library.'
11
+ gemspec.email = 'kpumuk@kpumuk.info'
12
+ gemspec.homepage = 'http://github.com/kpumuk/rlibsphinxclient'
13
+ gemspec.author = 'Dmytro Shteflyuk'
14
+ gemspec.extensions = ['ext/extconf.rb']
15
+ end
16
+ Jeweler::GemcutterTasks.new
17
+ rescue LoadError
18
+ puts 'Jeweler not available. Install it with: sudo gem install jeweler -s http://gemcutter.org'
19
+ end
20
+
21
+ desc 'Update SWIG wrapper for pure C searchd client API library'
22
+ task :swig do
23
+ system 'cd ext && swig -I/opt/local/include -I/opt/sphinx-0.9.9/include -ruby -autorename rlibsphinxclient.i'
24
+ end
25
+
26
+ desc 'Generate documentation for the rlibsphinxclient gem.'
27
+ Rake::RDocTask.new(:rdoc) do |rdoc|
28
+ rdoc.rdoc_dir = 'rdoc'
29
+ rdoc.title = 'rlibsphinxclient'
30
+ rdoc.options << '--line-numbers' << '--inline-source'
31
+ rdoc.rdoc_files.include('README.rdoc', 'CHANGELOG.rdoc', 'MIT-LICENSE')
32
+ rdoc.rdoc_files.include('lib/**/*.rb')
33
+ end
34
+
35
+ desc 'Builds a gem and installs it to ~/.gem folder. Used only for development purposes.'
36
+ task :dev do
37
+ system 'rake package'
38
+ system 'cd pkg && env ARCHFLAGS="-arch i386" DEBUG=1 gem install rlibsphinxclient --no-rdoc --no-ri -- --with-libsphinxclient-dir=/opt/sphinx-0.9.9'
39
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.2.2
data/ext/extconf.rb ADDED
@@ -0,0 +1,20 @@
1
+ require 'mkmf'
2
+
3
+ $CFLAGS.gsub! /-O\d/, ''
4
+
5
+ if ENV['DEBUG']
6
+ puts "setting debug flags"
7
+ $CFLAGS << " -O0 -ggdb -DHAVE_DEBUG"
8
+ else
9
+ $CFLAGS << " -O3"
10
+ end
11
+
12
+ find_library(*['sphinxclient', 'sphinx_create', '/opt/sphinx/lib'].compact) or
13
+ find_library(*['sphinxclient', 'sphinx_create', dir_config('libsphinxclient').last].compact) or
14
+ raise "shared library 'libsphinxclient' not found"
15
+
16
+ find_header(*['sphinxclient.h', '/opt/sphinx/include'].compact) or
17
+ find_header(*['sphinxclient.h', dir_config('libsphinxclient').first].compact) or
18
+ raise "header file 'sphinxclient.h' not found"
19
+
20
+ create_makefile 'rlibsphinxclient'
@@ -0,0 +1,314 @@
1
+ %module rlibsphinxclient
2
+ %{
3
+ #include <sphinxclient.h>
4
+ %}
5
+
6
+ %include "typemaps.i"
7
+
8
+ %newobject sphinx_create;
9
+
10
+ /* -----------------------------------------------------------------------------
11
+ * This section contains generic input parameter type mappings.
12
+ * ----------------------------------------------------------------------------- */
13
+
14
+ // Processing (sphinx_bool) params
15
+ %typemap(in) sphinx_bool {
16
+ switch(TYPE($input)) {
17
+ case T_TRUE:
18
+ case T_FALSE:
19
+ $1 = $input == Qtrue ? SPH_TRUE : SPH_FALSE;
20
+ break;
21
+ default:
22
+ SWIG_exception_fail(SWIG_TypeError, "in method '" "$symname" "', argument " "$argnum"" of type '" "$1_type""'");
23
+ break;
24
+ }
25
+ }
26
+
27
+ // Processing (sphinx_uint64_t *) params.
28
+ %typemap(in) sphinx_uint64_t * {
29
+ int size = RARRAY_LEN($input), i;
30
+
31
+ $1 = (sphinx_uint64_t *) malloc(size * sizeof(sphinx_uint64_t));
32
+ VALUE *ptr = RARRAY_PTR($input);
33
+ for (i = 0; i < size; i++, ptr++) {
34
+ $1[i] = NUM2ULL(*ptr);
35
+ }
36
+ }
37
+
38
+ // Cleaning up (sphinx_uint64_t *) params.
39
+ %typemap(freearg) sphinx_uint64_t * {
40
+ free((char *) $1);
41
+ }
42
+
43
+ // Processing (int *) params.
44
+ %typemap(in) int * {
45
+ int size = RARRAY_LEN($input), i;
46
+
47
+ $1 = (int *) malloc(size * sizeof(int));
48
+ VALUE *ptr = RARRAY_PTR($input);
49
+ for (i = 0; i < size; i++, ptr++) {
50
+ $1[i] = NUM2INT(*ptr);
51
+ }
52
+ }
53
+
54
+ // Cleaning up (int *) params.
55
+ %typemap(freearg) int * {
56
+ free((char *) $1);
57
+ }
58
+
59
+ // Processing (char **) params.
60
+ %typemap(in) char ** {
61
+ int size = RARRAY_LEN($input), i;
62
+
63
+ $1 = (char **) malloc(size * sizeof(char *));
64
+ VALUE *ptr = RARRAY_PTR($input);
65
+ for (i = 0; i < size; i++, ptr++) {
66
+ $1[i] = STR2CSTR(*ptr);
67
+ }
68
+ }
69
+
70
+ // Cleaning up (char **) params.
71
+ %typemap(freearg) char ** {
72
+ free((char *) $1);
73
+ }
74
+
75
+ /* -----------------------------------------------------------------------------
76
+ * This section contains sphinx_result return value type mappings
77
+ * (functions sphinx_query and sphinx_run_queries.)
78
+ * ----------------------------------------------------------------------------- */
79
+
80
+ // Processing sphinx_run_queries return value (array of sphinx_result).
81
+ %typemap(out) (sphinx_result *) {
82
+ int num_results = sphinx_get_num_results(arg1), i;
83
+
84
+ $result = rb_ary_new();
85
+ for (i = 0; i < num_results; i++) {
86
+ rb_ary_store($result, i, convert_sphinx_result(arg1, $1 + i));
87
+ }
88
+ }
89
+ sphinx_result * sphinx_run_queries(sphinx_client * client);
90
+
91
+ // Processing sphinx_query return value (single instance of sphinx_result).
92
+ %typemap(out) (sphinx_result *) {
93
+ $result = convert_sphinx_result(arg1, $1);
94
+ }
95
+
96
+ /* -----------------------------------------------------------------------------
97
+ * This section contains type mappings for sphinx_build_excerpts function.
98
+ * ----------------------------------------------------------------------------- */
99
+
100
+ // Processing sphinx_excerpt_options input parameter.
101
+ %typemap(in) sphinx_excerpt_options * (sphinx_excerpt_options opts, VALUE val) {
102
+ Check_Type($input, T_HASH);
103
+ sphinx_init_excerpt_options(&opts);
104
+
105
+ // before_match
106
+ val = rb_hash_aref($input, rb_str_new2("before_match"));
107
+ if (val != Qnil) {
108
+ Check_Type($input, T_STRING);
109
+ opts.before_match = STR2CSTR(val);
110
+ }
111
+
112
+ // after_match
113
+ val = rb_hash_aref($input, rb_str_new2("after_match"));
114
+ if (val != Qnil) {
115
+ Check_Type($input, T_STRING);
116
+ opts.after_match = STR2CSTR(val);
117
+ }
118
+
119
+ // chunk_separator
120
+ val = rb_hash_aref($input, rb_str_new2("chunk_separator"));
121
+ if (val != Qnil) {
122
+ Check_Type($input, T_STRING);
123
+ opts.chunk_separator = STR2CSTR(val);
124
+ }
125
+
126
+ // limit
127
+ val = rb_hash_aref($input, rb_str_new2("limit"));
128
+ if (val != Qnil) {
129
+ Check_Type($input, T_FIXNUM);
130
+ opts.limit = NUM2INT(val);
131
+ }
132
+
133
+ // around
134
+ val = rb_hash_aref($input, rb_str_new2("around"));
135
+ if (val != Qnil) {
136
+ Check_Type($input, T_FIXNUM);
137
+ opts.around = NUM2INT(val);
138
+ }
139
+
140
+ // exact_phrase
141
+ val = rb_hash_aref($input, rb_str_new2("exact_phrase"));
142
+ if (val != Qnil) {
143
+ opts.around = val == Qtrue ? SPH_TRUE : SPH_FALSE;
144
+ }
145
+
146
+ // single_passage
147
+ val = rb_hash_aref($input, rb_str_new2("single_passage"));
148
+ if (val != Qnil) {
149
+ opts.single_passage = val == Qtrue ? SPH_TRUE : SPH_FALSE;
150
+ }
151
+
152
+ // use_boundaries
153
+ val = rb_hash_aref($input, rb_str_new2("use_boundaries"));
154
+ if (val != Qnil) {
155
+ opts.use_boundaries = val == Qtrue ? SPH_TRUE : SPH_FALSE;
156
+ }
157
+
158
+ // weight_order
159
+ val = rb_hash_aref($input, rb_str_new2("weight_order"));
160
+ if (val != Qnil) {
161
+ opts.weight_order = val == Qtrue ? SPH_TRUE : SPH_FALSE;
162
+ }
163
+
164
+ $1 = &opts;
165
+ }
166
+
167
+ // Processing char ** output parameter.
168
+ %typemap(out) char ** {
169
+ int num_docs, i;
170
+
171
+ SWIG_AsVal_int(argv[1], &num_docs);
172
+
173
+ if ($1) {
174
+ $result = rb_ary_new();
175
+ for (i = 0; i < num_docs; i++) {
176
+ rb_ary_store($result, i, rb_str_new2($1[i]));
177
+
178
+ free((char *) $1[i]);
179
+ }
180
+ free((char *) $1);
181
+ } else {
182
+ $result = Qfalse;
183
+ }
184
+ }
185
+ char ** sphinx_build_excerpts(sphinx_client * client, int num_docs, const char ** docs, const char * index, const char * words, sphinx_excerpt_options * opts);
186
+ %typemap(in) sphinx_excerpt_options * {}
187
+ %typemap(out) char ** {}
188
+
189
+ /* -----------------------------------------------------------------------------
190
+ * This section contains type mappings for sphinx_build_keywords function.
191
+ * ----------------------------------------------------------------------------- */
192
+
193
+ // Defining out_num_keywords input parameter (will contain a number of keywords).
194
+ %typemap(in, numinputs = 0) int * out_num_keywords (int out_num_keywords) {
195
+ $1 = &out_num_keywords;
196
+ }
197
+
198
+ // Doing nothing because out_num_keywords is a local variable.
199
+ %typemap(freearg) int * out_num_keywords {
200
+ }
201
+
202
+ // Processing array of sphinx_keyword_info values.
203
+ %typemap(out) sphinx_keyword_info * {
204
+ int i;
205
+ VALUE keyword = Qnil;
206
+ $result = rb_ary_new();
207
+ for (i = 0; i < out_num_keywords5; i++) {
208
+ keyword = rb_hash_new();
209
+ rb_hash_aset(keyword, rb_str_new2("tokenized"), rb_str_new2($1[i].tokenized));
210
+ rb_hash_aset(keyword, rb_str_new2("normalized"), rb_str_new2($1[i].normalized));
211
+ rb_hash_aset(keyword, rb_str_new2("docs"), INT2FIX($1[i].num_docs));
212
+ rb_hash_aset(keyword, rb_str_new2("hits"), INT2FIX($1[i].num_hits));
213
+ rb_ary_store($result, i, keyword);
214
+
215
+ free((char *) $1[i].tokenized);
216
+ free((char *) $1[i].normalized);
217
+ }
218
+
219
+ free((char *) $1);
220
+ }
221
+
222
+ /* -----------------------------------------------------------------------------
223
+ * This section contains some useful helpers for type mappers.
224
+ * ----------------------------------------------------------------------------- */
225
+
226
+ %{
227
+ static VALUE convert_sphinx_result(sphinx_client *client, sphinx_result *input) {
228
+ int i, j, k;
229
+ VALUE result = Qnil, var1 = Qnil, var2 = Qnil, var3 = Qnil, var4 = Qnil;
230
+ char *msg = (char *) 0;
231
+ unsigned int *mva = 0;
232
+ char *time = 0;
233
+
234
+ if (!input) {
235
+ result = Qfalse;
236
+ } else {
237
+ result = rb_hash_new();
238
+ rb_hash_aset(result, rb_str_new2("error"), input->error ? rb_str_new2(input->error) : Qnil);
239
+ rb_hash_aset(result, rb_str_new2("warning"), input->warning ? rb_str_new2(input->warning) : Qnil);
240
+ rb_hash_aset(result, rb_str_new2("status"), INT2FIX(input->status));
241
+ if (input->status != SEARCHD_OK && input->status != SEARCHD_WARNING) {
242
+ return result;
243
+ }
244
+
245
+ rb_hash_aset(result, rb_str_new2("total"), INT2FIX(input->total));
246
+ rb_hash_aset(result, rb_str_new2("total_found"), INT2FIX(input->total_found));
247
+ time = (char *) malloc(20);
248
+ sprintf(time, "%.3f", input->time_msec / 1000.);
249
+ rb_hash_aset(result, rb_str_new2("time"), rb_str_new2(time));
250
+ free(time);
251
+
252
+ // fields
253
+ var1 = rb_ary_new();
254
+ for (i = 0; i < input->num_fields; i++) {
255
+ rb_ary_store(var1, i, rb_str_new2(input->fields[i]));
256
+ }
257
+ rb_hash_aset(result, rb_str_new2("fields"), var1);
258
+
259
+ // attrs
260
+ var1 = rb_hash_new();
261
+ for (i = 0; i < input->num_attrs; i++) {
262
+ rb_hash_aset(var1, rb_str_new2(input->attr_names[i]), INT2NUM(input->attr_types[i]));
263
+ }
264
+ rb_hash_aset(result, rb_str_new2("attrs"), var1);
265
+
266
+ // words
267
+ var1 = rb_hash_new();
268
+ for (i = 0; i < input->num_words; i++) {
269
+ var2 = rb_hash_new();
270
+ rb_hash_aset(var2, rb_str_new2("docs"), INT2FIX(input->words[i].docs));
271
+ rb_hash_aset(var2, rb_str_new2("hits"), INT2FIX(input->words[i].hits));
272
+
273
+ rb_hash_aset(var1, rb_str_new2(input->words[i].word), var2);
274
+ }
275
+ rb_hash_aset(result, rb_str_new2("words"), var1);
276
+
277
+ // matches
278
+ var1 = rb_ary_new();
279
+ for (i = 0; i < input->num_matches; i++) {
280
+ var2 = rb_hash_new();
281
+ rb_hash_aset(var2, rb_str_new2("id"), ULL2NUM(sphinx_get_id(input, i)));
282
+ rb_hash_aset(var2, rb_str_new2("weight"), INT2FIX(sphinx_get_weight(input, i)));
283
+
284
+ var3 = rb_hash_new();
285
+ for (j = 0; j < input->num_attrs; j++) {
286
+ switch (input->attr_types[j]) {
287
+ case SPH_ATTR_MULTI | SPH_ATTR_INTEGER:
288
+ mva = (unsigned int *) sphinx_get_mva(input, i, j);
289
+ var4 = rb_ary_new();
290
+ for (k = 0; k < (int) mva[0]; k++) {
291
+ rb_ary_store(var4, k, INT2NUM(mva[k + 1]));
292
+ }
293
+ break;
294
+ case SPH_ATTR_FLOAT:
295
+ var4 = rb_float_new(sphinx_get_float(input, i, j));
296
+ break;
297
+ default:
298
+ var4 = ULL2NUM(sphinx_get_int(input, i, j));
299
+ break;
300
+ }
301
+ rb_hash_aset(var3, rb_str_new2(input->attr_names[j]), var4);
302
+ }
303
+ rb_hash_aset(var2, rb_str_new2("attrs"), var3);
304
+
305
+ rb_ary_store(var1, i, var2);
306
+ }
307
+ rb_hash_aset(result, rb_str_new2("matches"), var1);
308
+ }
309
+
310
+ return result;
311
+ }
312
+ %}
313
+
314
+ %include "/opt/sphinx-0.9.9/include/sphinxclient.h"