rlibsphinxclient 0.2.2

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.
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"