sphinx 0.9.10.2122 → 2.1.1.3711
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -2
- data/.travis.yml +8 -0
- data/CHANGELOG.md +71 -0
- data/Gemfile +4 -0
- data/README.md +221 -0
- data/Rakefile +40 -35
- data/lib/sphinx.rb +3 -8
- data/lib/sphinx/client.rb +223 -70
- data/lib/sphinx/constants.rb +23 -8
- data/lib/sphinx/response.rb +6 -6
- data/lib/sphinx/timeout.rb +1 -2
- data/lib/sphinx/version.rb +3 -0
- data/spec/client_response_spec.rb +77 -64
- data/spec/client_spec.rb +68 -31
- data/spec/client_validations_spec.rb +61 -7
- data/spec/fixtures/requests/default_search.dat +0 -0
- data/spec/fixtures/requests/default_search_index.dat +0 -0
- data/spec/fixtures/requests/excerpt_custom.dat +0 -0
- data/spec/fixtures/requests/excerpt_default.dat +0 -0
- data/spec/fixtures/requests/excerpt_flags.dat +0 -0
- data/spec/fixtures/requests/field_weights.dat +0 -0
- data/spec/fixtures/requests/filter.dat +0 -0
- data/spec/fixtures/requests/filter_exclude.dat +0 -0
- data/spec/fixtures/requests/filter_float_range.dat +0 -0
- data/spec/fixtures/requests/filter_float_range_exclude.dat +0 -0
- data/spec/fixtures/requests/filter_range.dat +0 -0
- data/spec/fixtures/requests/filter_range_exclude.dat +0 -0
- data/spec/fixtures/requests/filter_range_int64.dat +0 -0
- data/spec/fixtures/requests/filter_ranges.dat +0 -0
- data/spec/fixtures/requests/filters.dat +0 -0
- data/spec/fixtures/requests/filters_different.dat +0 -0
- data/spec/fixtures/requests/geo_anchor.dat +0 -0
- data/spec/fixtures/requests/group_by_attr.dat +0 -0
- data/spec/fixtures/requests/group_by_attrpair.dat +0 -0
- data/spec/fixtures/requests/group_by_day.dat +0 -0
- data/spec/fixtures/requests/group_by_day_sort.dat +0 -0
- data/spec/fixtures/requests/group_by_month.dat +0 -0
- data/spec/fixtures/requests/group_by_week.dat +0 -0
- data/spec/fixtures/requests/group_by_year.dat +0 -0
- data/spec/fixtures/requests/group_distinct.dat +0 -0
- data/spec/fixtures/requests/id_range.dat +0 -0
- data/spec/fixtures/requests/id_range64.dat +0 -0
- data/spec/fixtures/requests/index_weights.dat +0 -0
- data/spec/fixtures/requests/keywords.dat +0 -0
- data/spec/fixtures/requests/limits.dat +0 -0
- data/spec/fixtures/requests/limits_cutoff.dat +0 -0
- data/spec/fixtures/requests/limits_max.dat +0 -0
- data/spec/fixtures/requests/limits_max_cutoff.dat +0 -0
- data/spec/fixtures/requests/match_all.dat +0 -0
- data/spec/fixtures/requests/match_any.dat +0 -0
- data/spec/fixtures/requests/match_boolean.dat +0 -0
- data/spec/fixtures/requests/match_extended.dat +0 -0
- data/spec/fixtures/requests/match_extended2.dat +0 -0
- data/spec/fixtures/requests/match_fullscan.dat +0 -0
- data/spec/fixtures/requests/match_phrase.dat +0 -0
- data/spec/fixtures/requests/max_query_time.dat +0 -0
- data/spec/fixtures/requests/miltiple_queries.dat +0 -0
- data/spec/fixtures/requests/outer_select.dat +0 -0
- data/spec/fixtures/requests/override.dat +0 -0
- data/spec/fixtures/{default_search.php → requests/php/default_search.php} +1 -1
- data/spec/fixtures/{default_search_index.php → requests/php/default_search_index.php} +1 -1
- data/spec/fixtures/{excerpt_custom.php → requests/php/excerpt_custom.php} +1 -1
- data/spec/fixtures/{excerpt_default.php → requests/php/excerpt_default.php} +1 -1
- data/spec/fixtures/{excerpt_flags.php → requests/php/excerpt_flags.php} +1 -1
- data/spec/fixtures/{field_weights.php → requests/php/field_weights.php} +1 -1
- data/spec/fixtures/{filter.php → requests/php/filter.php} +1 -1
- data/spec/fixtures/{filter_exclude.php → requests/php/filter_exclude.php} +1 -1
- data/spec/fixtures/{filter_float_range.php → requests/php/filter_float_range.php} +1 -1
- data/spec/fixtures/{filter_float_range_exclude.php → requests/php/filter_float_range_exclude.php} +1 -1
- data/spec/fixtures/{filter_range.php → requests/php/filter_range.php} +1 -1
- data/spec/fixtures/{filter_range_exclude.php → requests/php/filter_range_exclude.php} +1 -1
- data/spec/fixtures/{filter_range_int64.php → requests/php/filter_range_int64.php} +1 -1
- data/spec/fixtures/{filter_ranges.php → requests/php/filter_ranges.php} +1 -1
- data/spec/fixtures/{filters.php → requests/php/filters.php} +1 -1
- data/spec/fixtures/{filters_different.php → requests/php/filters_different.php} +1 -1
- data/spec/fixtures/{geo_anchor.php → requests/php/geo_anchor.php} +1 -1
- data/spec/fixtures/{group_by_attr.php → requests/php/group_by_attr.php} +1 -1
- data/spec/fixtures/{group_by_attrpair.php → requests/php/group_by_attrpair.php} +1 -1
- data/spec/fixtures/{group_by_day.php → requests/php/group_by_day.php} +1 -1
- data/spec/fixtures/{group_by_day_sort.php → requests/php/group_by_day_sort.php} +1 -1
- data/spec/fixtures/{group_by_month.php → requests/php/group_by_month.php} +1 -1
- data/spec/fixtures/{group_by_week.php → requests/php/group_by_week.php} +1 -1
- data/spec/fixtures/{group_by_year.php → requests/php/group_by_year.php} +1 -1
- data/spec/fixtures/{group_distinct.php → requests/php/group_distinct.php} +1 -1
- data/spec/fixtures/{id_range.php → requests/php/id_range.php} +1 -1
- data/spec/fixtures/{id_range64.php → requests/php/id_range64.php} +1 -1
- data/spec/fixtures/{index_weights.php → requests/php/index_weights.php} +1 -1
- data/spec/fixtures/{keywords.php → requests/php/keywords.php} +1 -1
- data/spec/fixtures/{limits.php → requests/php/limits.php} +1 -1
- data/spec/fixtures/{limits_cutoff.php → requests/php/limits_cutoff.php} +1 -1
- data/spec/fixtures/{limits_max.php → requests/php/limits_max.php} +1 -1
- data/spec/fixtures/{limits_max_cutoff.php → requests/php/limits_max_cutoff.php} +1 -1
- data/spec/fixtures/{match_all.php → requests/php/match_all.php} +1 -1
- data/spec/fixtures/{match_any.php → requests/php/match_any.php} +1 -1
- data/spec/fixtures/{match_boolean.php → requests/php/match_boolean.php} +1 -1
- data/spec/fixtures/{match_extended.php → requests/php/match_extended.php} +1 -1
- data/spec/fixtures/{match_extended2.php → requests/php/match_extended2.php} +1 -1
- data/spec/fixtures/{match_fullscan.php → requests/php/match_fullscan.php} +1 -1
- data/spec/fixtures/{match_phrase.php → requests/php/match_phrase.php} +1 -1
- data/spec/fixtures/{max_query_time.php → requests/php/max_query_time.php} +1 -1
- data/spec/fixtures/{miltiple_queries.php → requests/php/miltiple_queries.php} +1 -1
- data/spec/fixtures/requests/php/outer_select.php +9 -0
- data/spec/fixtures/{set_override.php → requests/php/override.php} +1 -1
- data/spec/fixtures/requests/php/query_flag.php +13 -0
- data/spec/fixtures/requests/php/query_flag_after_reset.php +19 -0
- data/spec/fixtures/{ranking_bm25.php → requests/php/ranking_bm25.php} +1 -1
- data/spec/fixtures/requests/php/ranking_expr.php +9 -0
- data/spec/fixtures/{ranking_fieldmask.php → requests/php/ranking_fieldmask.php} +1 -1
- data/spec/fixtures/{ranking_matchany.php → requests/php/ranking_matchany.php} +1 -1
- data/spec/fixtures/{ranking_none.php → requests/php/ranking_none.php} +1 -1
- data/spec/fixtures/{ranking_proximity.php → requests/php/ranking_proximity.php} +1 -1
- data/spec/fixtures/{ranking_proximity_bm25.php → requests/php/ranking_proximity_bm25.php} +1 -1
- data/spec/fixtures/{ranking_sph04.php → requests/php/ranking_sph04.php} +1 -1
- data/spec/fixtures/{ranking_wordcount.php → requests/php/ranking_wordcount.php} +1 -1
- data/spec/fixtures/{retries.php → requests/php/retries.php} +1 -1
- data/spec/fixtures/{retries_delay.php → requests/php/retries_delay.php} +1 -1
- data/spec/fixtures/{select.php → requests/php/select.php} +1 -1
- data/spec/fixtures/{sort_attr_asc.php → requests/php/sort_attr_asc.php} +1 -1
- data/spec/fixtures/{sort_attr_desc.php → requests/php/sort_attr_desc.php} +1 -1
- data/spec/fixtures/{sort_expr.php → requests/php/sort_expr.php} +1 -1
- data/spec/fixtures/{sort_extended.php → requests/php/sort_extended.php} +1 -1
- data/spec/fixtures/{sort_relevance.php → requests/php/sort_relevance.php} +1 -1
- data/spec/fixtures/{sort_time_segments.php → requests/php/sort_time_segments.php} +1 -1
- data/spec/fixtures/{update_attributes.php → requests/php/update_attributes.php} +1 -1
- data/spec/fixtures/{update_attributes_mva.php → requests/php/update_attributes_mva.php} +1 -1
- data/spec/fixtures/{weights.php → requests/php/weights.php} +1 -1
- data/spec/fixtures/requests/query_flag.dat +0 -0
- data/spec/fixtures/requests/query_flag_after_reset.dat +0 -0
- data/spec/fixtures/requests/ranking_bm25.dat +0 -0
- data/spec/fixtures/requests/ranking_expr.dat +0 -0
- data/spec/fixtures/requests/ranking_fieldmask.dat +0 -0
- data/spec/fixtures/requests/ranking_matchany.dat +0 -0
- data/spec/fixtures/requests/ranking_none.dat +0 -0
- data/spec/fixtures/requests/ranking_proximity.dat +0 -0
- data/spec/fixtures/requests/ranking_proximity_bm25.dat +0 -0
- data/spec/fixtures/requests/ranking_sph04.dat +0 -0
- data/spec/fixtures/requests/ranking_wordcount.dat +0 -0
- data/spec/fixtures/requests/retries.dat +0 -0
- data/spec/fixtures/requests/retries_delay.dat +0 -0
- data/spec/fixtures/requests/select.dat +0 -0
- data/spec/fixtures/requests/sort_attr_asc.dat +0 -0
- data/spec/fixtures/requests/sort_attr_desc.dat +0 -0
- data/spec/fixtures/requests/sort_expr.dat +0 -0
- data/spec/fixtures/requests/sort_extended.dat +0 -0
- data/spec/fixtures/requests/sort_relevance.dat +0 -0
- data/spec/fixtures/requests/sort_time_segments.dat +0 -0
- data/spec/fixtures/requests/update_attributes.dat +0 -0
- data/spec/fixtures/requests/update_attributes_mva.dat +0 -0
- data/spec/fixtures/requests/weights.dat +0 -0
- data/spec/fixtures/responses/build_excerpts.dat +0 -0
- data/spec/fixtures/responses/build_keywords.dat +0 -0
- data/spec/fixtures/responses/flush_attributes.dat +0 -0
- data/spec/fixtures/responses/flush_attrs.dat +2 -0
- data/spec/fixtures/responses/open.dat +0 -0
- data/spec/fixtures/responses/open_twice.dat +0 -0
- data/spec/fixtures/responses/php/build_excerpts.php +8 -0
- data/spec/fixtures/responses/php/build_keywords.php +8 -0
- data/spec/fixtures/responses/php/flush_attributes.php +8 -0
- data/spec/fixtures/responses/php/open.php +8 -0
- data/spec/fixtures/responses/php/open_twice.php +9 -0
- data/spec/fixtures/responses/php/query.php +8 -0
- data/spec/fixtures/responses/php/query_error.php +8 -0
- data/spec/fixtures/responses/php/query_id64.php +8 -0
- data/spec/fixtures/responses/php/run_queries.php +10 -0
- data/spec/fixtures/responses/php/run_queries_error.php +9 -0
- data/spec/fixtures/responses/php/status.php +8 -0
- data/spec/fixtures/responses/php/update_attributes.php +8 -0
- data/spec/fixtures/responses/php/update_attributes_mva.php +8 -0
- data/spec/fixtures/responses/query.dat +0 -0
- data/spec/fixtures/responses/query_error.dat +0 -0
- data/spec/fixtures/responses/query_id64.dat +0 -0
- data/spec/fixtures/responses/run_queries.dat +0 -0
- data/spec/fixtures/responses/run_queries_error.dat +0 -0
- data/spec/fixtures/responses/status.dat +0 -0
- data/spec/fixtures/responses/update_attributes.dat +0 -0
- data/spec/fixtures/responses/update_attributes_mva.dat +0 -0
- data/spec/fixtures/sphinxapi.php +217 -45
- data/spec/spec_helper.rb +18 -3
- data/spec/sphinx/sphinx-id64.conf +6 -6
- data/spec/sphinx/sphinx.conf +6 -6
- data/sphinx.gemspec +19 -121
- metadata +268 -105
- data/README.rdoc +0 -243
- data/VERSION.yml +0 -5
- data/init.rb +0 -1
data/.gitignore
CHANGED
data/.travis.yml
ADDED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
## 2.1.1.3711 (Mar 15, 2013)
|
2
|
+
|
3
|
+
Features:
|
4
|
+
|
5
|
+
- Updated API to the latest Sphinx version 2.1.1.
|
6
|
+
- Refactored specs so they don't require running Sphinx instance and php.
|
7
|
+
|
8
|
+
## 0.9.10.2122 (Dec 04, 2009)
|
9
|
+
|
10
|
+
Features:
|
11
|
+
|
12
|
+
- Sphinx::Client#escape_string method added.
|
13
|
+
|
14
|
+
Bugfixes:
|
15
|
+
|
16
|
+
- Allow empty array or single integer in #set_filter as values.
|
17
|
+
|
18
|
+
## 0.9.10.2094 (Nov 23, 2009)
|
19
|
+
|
20
|
+
Features:
|
21
|
+
|
22
|
+
- Added logging.
|
23
|
+
- Added ability to pass a block to Client#query method to set request parameters.
|
24
|
+
- Use CRC32 of the request to select the server.
|
25
|
+
- Results returned in an instance of HashWithIndifferentAccess.
|
26
|
+
|
27
|
+
## 0.9.10.2091 (Nov 20, 2009)
|
28
|
+
|
29
|
+
Features:
|
30
|
+
|
31
|
+
- Added Ruby-style named methods in addition to native Sphinx API naming.
|
32
|
+
- Return `Sphinx::Client` object itself from any `set_` method to allow chaining.
|
33
|
+
- Status() API call queries all configured servers.
|
34
|
+
|
35
|
+
## 0.9.10.2086 (Nov 19, 2009)
|
36
|
+
|
37
|
+
Bugfixes:
|
38
|
+
|
39
|
+
- Better documentation.
|
40
|
+
- Fixed incomplete reply handling.
|
41
|
+
- Sphinx IANA assigned ports are 9312 and 9306 respectively (goodbye, trusty 3312)
|
42
|
+
|
43
|
+
## 0.9.10.2043 (Nov 16, 2009)
|
44
|
+
|
45
|
+
Features:
|
46
|
+
|
47
|
+
- Updated Sphinx API to version 0.9.10.
|
48
|
+
- Added ability to set multiple servers.
|
49
|
+
|
50
|
+
Bugfixes:
|
51
|
+
|
52
|
+
- Better argument validation.
|
53
|
+
- Properly handle connection timeouts.
|
54
|
+
- Added request timeout handling and retries.
|
55
|
+
- Close TCP socket on connection failure.
|
56
|
+
|
57
|
+
## 0.5.0.1112 (Aug 4, 2008)
|
58
|
+
|
59
|
+
Features:
|
60
|
+
|
61
|
+
- Updated Sphinx API to version 0.9.9.
|
62
|
+
|
63
|
+
Bugfixes:
|
64
|
+
|
65
|
+
- Fixed support of 64-bit values.
|
66
|
+
|
67
|
+
## 0.4.0.1112 (May 2, 2008)
|
68
|
+
|
69
|
+
Features:
|
70
|
+
|
71
|
+
- Initial implementation of Sphinx API for Sphinx 0.9.8.
|
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,221 @@
|
|
1
|
+
# Sphinx Client API
|
2
|
+
|
3
|
+
[![Travis-CI build status](https://secure.travis-ci.org/kpumuk/sphinx.png)](http://travis-ci.org/kpumuk/sphinx)
|
4
|
+
|
5
|
+
This document gives an overview of what is Sphinx itself and how to use it
|
6
|
+
from your Ruby on Rails application. For more information about Sphinx and
|
7
|
+
its API documentation visit [sphinxsearch.com](http://www.sphinxsearch.com).
|
8
|
+
|
9
|
+
## Sphinx
|
10
|
+
|
11
|
+
Sphinx is a standalone full-text search engine, meant to provide fast,
|
12
|
+
size-efficient and relevant fulltext search functions to other applications.
|
13
|
+
Sphinx was specially designed to integrate well with SQL databases and
|
14
|
+
scripting languages. Currently built-in data sources support fetching data
|
15
|
+
either via direct connection to MySQL, or from an XML pipe.
|
16
|
+
|
17
|
+
Simplest way to communicate with Sphinx is to use `searchd` —
|
18
|
+
a daemon to search through full text indexes from external software.
|
19
|
+
|
20
|
+
## Installation
|
21
|
+
|
22
|
+
Add the "sphinx" gem to your `Gemfile`.
|
23
|
+
|
24
|
+
gem 'sphinx'
|
25
|
+
|
26
|
+
And run `bundle install` command.
|
27
|
+
|
28
|
+
## Documentation
|
29
|
+
|
30
|
+
Complete Sphinx plugin documentation could be found on [GitHub Pages](http://kpumuk.github.com/sphinx).
|
31
|
+
|
32
|
+
Also you can find documentation on [rdoc.info](http://rdoc.info/projects/kpumuk/sphinx).
|
33
|
+
|
34
|
+
You can build the documentation locally by running:
|
35
|
+
|
36
|
+
rake yard
|
37
|
+
|
38
|
+
Complete Sphinx API documentation could be found on [Sphinx Search Engine
|
39
|
+
site](http://www.sphinxsearch.com/docs/current.html).
|
40
|
+
This plugin is fully compatible with original PHP API implementation.
|
41
|
+
|
42
|
+
## Ruby naming conventions
|
43
|
+
|
44
|
+
Sphinx Client API supports Ruby naming conventions, so every API
|
45
|
+
method name is in underscored, lowercase form:
|
46
|
+
|
47
|
+
SetServer -> set_server
|
48
|
+
RunQueries -> run_queries
|
49
|
+
SetMatchMode -> set_match_mode
|
50
|
+
|
51
|
+
Every method is aliased to a corresponding one from standard Sphinx
|
52
|
+
API, so you can use both `SetServer` and `set_server`
|
53
|
+
with no differrence.
|
54
|
+
|
55
|
+
There are three exceptions to this naming rule:
|
56
|
+
|
57
|
+
GetLastError -> last_error
|
58
|
+
GetLastWarning -> last_warning
|
59
|
+
IsConnectError -> connect_error?
|
60
|
+
|
61
|
+
Of course, all of them are aliased to the original method names.
|
62
|
+
|
63
|
+
## Using multiple Sphinx servers
|
64
|
+
|
65
|
+
Since we actively use this plugin in our Scribd development workflow,
|
66
|
+
there are several methods have been added to accommodate our needs.
|
67
|
+
You can find documentation on Ruby-specific methods in [documentation](http://rdoc.info/projects/kpumuk/sphinx).
|
68
|
+
|
69
|
+
First of all, we added support of multiple Sphinx servers to balance
|
70
|
+
load between them. Also it means that in case of any problems with one
|
71
|
+
of servers, library will try to fetch the results from another one.
|
72
|
+
Every consequence request will be executed on the next server in list
|
73
|
+
(round-robin technique).
|
74
|
+
|
75
|
+
sphinx.set_servers([
|
76
|
+
{ :host => 'browse01.local', :port => 3312 },
|
77
|
+
{ :host => 'browse02.local', :port => 3312 },
|
78
|
+
{ :host => 'browse03.local', :port => 3312 }
|
79
|
+
])
|
80
|
+
|
81
|
+
By default library will try to fetch results from a single server, and
|
82
|
+
fail if it does not respond. To setup number of retries being performed,
|
83
|
+
you can use second (additional) parameter of the `set_connect_timeout`
|
84
|
+
and `set_request_timeout` methods:
|
85
|
+
|
86
|
+
sphinx.set_connect_timeout(1, 3)
|
87
|
+
sphinx.set_request_timeout(1, 3)
|
88
|
+
|
89
|
+
There is a big difference between these two methods. First will affect
|
90
|
+
only on requests experiencing problems with connection (socket error,
|
91
|
+
pipe error, etc), second will be used when request is broken somehow
|
92
|
+
(temporary searchd error, incomplete reply, etc). The workflow looks like
|
93
|
+
this:
|
94
|
+
|
95
|
+
1. Increase retries number. If is less or equal to configured value,
|
96
|
+
try to connect to the next server. Otherwise, raise an error.
|
97
|
+
2. In case of connection problem go to 1.
|
98
|
+
3. Increase request retries number. If it less or equal to configured
|
99
|
+
value, try to perform request. Otherwise, raise an error.
|
100
|
+
4. In case of connection problem go to 1.
|
101
|
+
5. In case of request problem, go to 3.
|
102
|
+
6. Parse and return response.
|
103
|
+
|
104
|
+
Withdrawals:
|
105
|
+
|
106
|
+
1. Request could be performed `connect_retries` * `request_retries`
|
107
|
+
times. E.g., it could be tried `request_retries` times on each
|
108
|
+
of `connect_retries` servers (when you have 1 server configured,
|
109
|
+
but `connect_retries` is 5, library will try to connect to this
|
110
|
+
server 5 times).
|
111
|
+
2. Request could be tried to execute on each server `1..request_retries`
|
112
|
+
times. In case of connection problem, request will be moved to another
|
113
|
+
server immediately.
|
114
|
+
|
115
|
+
Usually you will set `connect_retries` equal to servers number,
|
116
|
+
so you will be sure each failing request will be performed on all servers.
|
117
|
+
This means that if one of servers is live, but others are dead, you request
|
118
|
+
will be finally executed successfully.
|
119
|
+
|
120
|
+
## Sphinx constants
|
121
|
+
|
122
|
+
Most Sphinx API methods expecting for special constants will be passed.
|
123
|
+
For example:
|
124
|
+
|
125
|
+
sphinx.set_match_mode(Sphinx::SPH_MATCH_ANY)
|
126
|
+
|
127
|
+
Please note that these constants defined in a `Sphinx`
|
128
|
+
module. You can use symbols or strings instead of these awful
|
129
|
+
constants:
|
130
|
+
|
131
|
+
sphinx.set_match_mode(:any)
|
132
|
+
sphinx.set_match_mode('any')
|
133
|
+
|
134
|
+
## Setting query filters
|
135
|
+
|
136
|
+
Every `set_` method returns `Sphinx::Client` object itself.
|
137
|
+
It means that you can chain filtering methods:
|
138
|
+
|
139
|
+
results = Sphinx::Client.new.
|
140
|
+
set_match_mode(:any).
|
141
|
+
set_ranking_mode(:bm25).
|
142
|
+
set_id_range(10, 1000).
|
143
|
+
query('test')
|
144
|
+
|
145
|
+
There is a handful ability to set query parameters directly in `query`
|
146
|
+
call. If block does not accept any parameters, it will be eval'ed inside
|
147
|
+
Sphinx::Client instance:
|
148
|
+
|
149
|
+
results = Sphinx::Client.new.query('test') do
|
150
|
+
match_mode :any
|
151
|
+
ranking_mode :bm25
|
152
|
+
id_range 10, 1000
|
153
|
+
end
|
154
|
+
|
155
|
+
As you can see, in this case you can omit the `set_` prefix for
|
156
|
+
this methods. If block accepts a parameter, sphinx instance will be
|
157
|
+
passed into the block. In this case you should you full method names
|
158
|
+
including the `set_` prefix:
|
159
|
+
|
160
|
+
results = Sphinx::Client.new.query('test') do |sphinx|
|
161
|
+
sphinx.set_match_mode :any
|
162
|
+
sphinx.set_ranking_mode :bm25
|
163
|
+
sphinx.set_id_range 10, 1000
|
164
|
+
end
|
165
|
+
|
166
|
+
## Example
|
167
|
+
|
168
|
+
This simple example illustrates base connection establishing,
|
169
|
+
search results retrieving, and excerpts building. Please note
|
170
|
+
how does it perform database select using ActiveRecord to
|
171
|
+
save the order of records established by Sphinx.
|
172
|
+
|
173
|
+
sphinx = Sphinx::Client.new
|
174
|
+
result = sphinx.query('test')
|
175
|
+
ids = result['matches'].map { |match| match['id'] }
|
176
|
+
posts = Post.all :conditions => { :id => ids },
|
177
|
+
:order => "FIELD(id,#{ids.join(',')})"
|
178
|
+
|
179
|
+
docs = posts.map(&:body)
|
180
|
+
excerpts = sphinx.build_excerpts(docs, 'index', 'test')
|
181
|
+
|
182
|
+
## Logging
|
183
|
+
|
184
|
+
You can ask Sphinx client API to log it's activity to some log. In
|
185
|
+
order to do that you can pass a logger object into the `Sphinx::Client`
|
186
|
+
constructor:
|
187
|
+
|
188
|
+
require 'logger'
|
189
|
+
Sphinx::Client.new(Logger.new(STDOUT)).query('test')
|
190
|
+
|
191
|
+
Logger object should respond to methods :debug, :info, and :warn, and
|
192
|
+
accept blocks (this is what standard Ruby `Logger` class does).
|
193
|
+
Here is what you will see in your log:
|
194
|
+
|
195
|
+
* `DEBUG` -- `query`, `add_query`, `run_queries`
|
196
|
+
method calls with configured filters.
|
197
|
+
* `INFO` -- initialization with Sphinx version, servers change,
|
198
|
+
attempts to re-connect, and all attempts to do an API call with server
|
199
|
+
where request being performed.
|
200
|
+
* `WARN` -- various connection and socket errors.
|
201
|
+
|
202
|
+
## Support
|
203
|
+
|
204
|
+
You can find source code for this library on [GitHub](http://github.com/kpumuk/sphinx).
|
205
|
+
|
206
|
+
To suggest a feature or report a bug use [GitHub Issues](http://github.com/kpumuk/sphinx/issues)
|
207
|
+
|
208
|
+
## Credits
|
209
|
+
|
210
|
+
* [Dmytro Shteflyuk](https://github.com/kpumuk) (author)
|
211
|
+
* [Andrew Aksyonoff](http://sphinxsearch.com) (Sphinx core developer)
|
212
|
+
|
213
|
+
Special thanks to [Alexey Kovyrin](https://github.com/kovyrin)
|
214
|
+
|
215
|
+
Special thanks to [Mike Perham](https://github.com/mperham) for his awesome
|
216
|
+
memcache-client gem, where latest Sphinx gem got new sockets handling from.
|
217
|
+
|
218
|
+
## License
|
219
|
+
|
220
|
+
This library is distributed under the terms of the Ruby license.
|
221
|
+
You can freely distribute/modify this library.
|
data/Rakefile
CHANGED
@@ -1,45 +1,50 @@
|
|
1
|
-
require '
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
1
|
+
require 'bundler'
|
2
|
+
Bundler::GemHelper.install_tasks
|
3
|
+
|
4
|
+
require 'rspec/core/rake_task'
|
5
|
+
RSpec::Core::RakeTask.new(:spec)
|
6
|
+
|
7
|
+
task :test => :spec
|
8
|
+
task :default => :spec
|
9
|
+
|
10
|
+
require 'yard'
|
11
|
+
YARD::Rake::YardocTask.new(:yard) do |t|
|
12
|
+
t.options = ['--title', 'Sphinx Client API Documentation']
|
13
|
+
if ENV['PRIVATE']
|
14
|
+
t.options.concat ['--protected', '--private']
|
15
|
+
else
|
16
|
+
t.options.concat ['--protected', '--no-private']
|
12
17
|
end
|
13
|
-
Jeweler::GemcutterTasks.new
|
14
|
-
rescue LoadError
|
15
|
-
puts 'Jeweler not available. Install it with: sudo gem install jeweler'
|
16
18
|
end
|
17
19
|
|
18
|
-
|
19
|
-
|
20
|
+
require 'bundler'
|
21
|
+
Bundler::GemHelper.install_tasks
|
20
22
|
|
21
|
-
|
22
|
-
|
23
|
+
namespace :fixtures do
|
24
|
+
FIXTURES_DIR = File.expand_path('../spec/fixtures', __FILE__)
|
23
25
|
|
24
|
-
desc '
|
25
|
-
|
26
|
-
|
27
|
-
|
26
|
+
desc 'Update textures for sphinx requests'
|
27
|
+
task :requests do
|
28
|
+
rm Dir.glob("#{FIXTURES_DIR}/requests/*.dat")
|
29
|
+
Dir["#{FIXTURES_DIR}/requests/php/*.php"].each do |file|
|
30
|
+
puts name = File.basename(file, '.php')
|
31
|
+
File.open(File.join(File.dirname(file), '..', "#{name}.dat"), 'w') do |f|
|
32
|
+
f.write `env SPHINX_MOCK_REQUEST=1 php "#{file}"`
|
33
|
+
end
|
34
|
+
end
|
28
35
|
end
|
29
|
-
rescue LoadError
|
30
|
-
puts 'RSpec not available. Install it with: sudo gem install rspec'
|
31
|
-
end
|
32
36
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
37
|
+
desc 'Update textures for sphinx responses'
|
38
|
+
task :responses do
|
39
|
+
rm Dir.glob("#{FIXTURES_DIR}/responses/*.dat")
|
40
|
+
Dir["#{FIXTURES_DIR}/responses/php/*.php"].each do |file|
|
41
|
+
puts name = File.basename(file, '.php')
|
42
|
+
File.open(File.join(File.dirname(file), '..', "#{name}.dat"), 'w') do |f|
|
43
|
+
f.write `env SPHINX_MOCK_RESPONSE=1 php "#{file}"`
|
44
|
+
end
|
41
45
|
end
|
42
46
|
end
|
43
|
-
rescue LoadError
|
44
|
-
puts 'Yard not available. Install it with: sudo gem install yard'
|
45
47
|
end
|
48
|
+
|
49
|
+
desc 'Update binary fixtures'
|
50
|
+
task :fixtures => %w[ fixtures:requests fixtures:responses]
|
data/lib/sphinx.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
# Sphinx Client API
|
2
2
|
#
|
3
3
|
# Author:: Dmytro Shteflyuk <mailto:kpumuk@kpumuk.info>.
|
4
|
-
# Copyright:: Copyright (c) 2006 —
|
4
|
+
# Copyright:: Copyright (c) 2006 — 2013 Dmytro Shteflyuk
|
5
5
|
# License:: Distributes under the same terms as Ruby
|
6
|
-
# Version:: 0.9.10
|
6
|
+
# Version:: 0.9.10.2122
|
7
7
|
# Website:: http://kpumuk.info/projects/ror-plugins/sphinx
|
8
8
|
# Sources:: http://github.com/kpumuk/sphinx
|
9
9
|
#
|
@@ -11,11 +11,6 @@
|
|
11
11
|
# You can freely distribute/modify this library.
|
12
12
|
#
|
13
13
|
module Sphinx
|
14
|
-
VERSION = begin
|
15
|
-
config = YAML.load(File.read(File.dirname(__FILE__) + '/../VERSION.yml'))
|
16
|
-
"#{config[:major]}.#{config[:minor]}.#{config[:patch]}.#{config[:build]}"
|
17
|
-
end
|
18
|
-
|
19
14
|
# Base class for all Sphinx errors
|
20
15
|
class SphinxError < StandardError; end
|
21
16
|
|
@@ -40,8 +35,8 @@ require 'socket'
|
|
40
35
|
require 'zlib'
|
41
36
|
|
42
37
|
path = File.dirname(__FILE__)
|
43
|
-
require "#{path}/sphinx/constants"
|
44
38
|
require "#{path}/sphinx/indifferent_access"
|
39
|
+
require "#{path}/sphinx/constants"
|
45
40
|
require "#{path}/sphinx/request"
|
46
41
|
require "#{path}/sphinx/response"
|
47
42
|
require "#{path}/sphinx/timeout"
|
data/lib/sphinx/client.rb
CHANGED
@@ -14,7 +14,7 @@ module Sphinx
|
|
14
14
|
#
|
15
15
|
class Client
|
16
16
|
include Sphinx::Constants
|
17
|
-
|
17
|
+
|
18
18
|
#=================================================================
|
19
19
|
# Some internal attributes to use inside client API
|
20
20
|
#=================================================================
|
@@ -66,10 +66,17 @@ module Sphinx
|
|
66
66
|
@anchor = [] # geographical anchor point
|
67
67
|
@indexweights = [] # per-index weights
|
68
68
|
@ranker = SPH_RANK_PROXIMITY_BM25 # ranking mode (default is SPH_RANK_PROXIMITY_BM25)
|
69
|
+
@rankexpr = '' # ranking expression
|
69
70
|
@maxquerytime = 0 # max query time, milliseconds (default is 0, do not limit)
|
70
71
|
@fieldweights = {} # per-field-name weights
|
71
72
|
@overrides = [] # per-query attribute values overrides
|
72
73
|
@select = '*' # select-list (attributes or expressions, with optional aliases)
|
74
|
+
@query_flags = 0
|
75
|
+
@predictedtime = 0
|
76
|
+
@outerorderby = ''
|
77
|
+
@outeroffset = 0
|
78
|
+
@outerlimit = 0
|
79
|
+
@hasouter = false
|
73
80
|
|
74
81
|
# per-reply fields (for single-query case)
|
75
82
|
@error = '' # last error message
|
@@ -107,8 +114,8 @@ module Sphinx
|
|
107
114
|
:overrides => @overrides,
|
108
115
|
:select => @select,
|
109
116
|
:match_mode => @mode,
|
110
|
-
:
|
111
|
-
:sort_mode => { :mode => @sort, :
|
117
|
+
:ranking => { :mode => @ranker, :expression => @rankexpr },
|
118
|
+
:sort_mode => { :mode => @sort, :sort_by => @sortby },
|
112
119
|
:weights => @weights,
|
113
120
|
:field_weights => @fieldweights,
|
114
121
|
:index_weights => @indexweights,
|
@@ -116,11 +123,12 @@ module Sphinx
|
|
116
123
|
:filters => @filters,
|
117
124
|
:geo_anchor => @anchor,
|
118
125
|
:group_by => { :attribute => @groupby, :func => @groupfunc, :sort => @groupsort },
|
119
|
-
:group_distinct => @groupdistinct
|
126
|
+
:group_distinct => @groupdistinct,
|
127
|
+
:query_flags => { :bitset => @query_flags, :predicted_time => @predictedtime },
|
128
|
+
:outer_select => { :has_outer => @hasouter, :sort_by => @outerorderby, :offset => @outeroffset, :limit => @outerlimit},
|
120
129
|
}
|
121
130
|
|
122
|
-
"<Sphinx::Client: %d servers, params: %s>" %
|
123
|
-
[@servers.length, params.inspect]
|
131
|
+
"<Sphinx::Client: %d servers, params: %s>" % [@servers.length, params.inspect]
|
124
132
|
end
|
125
133
|
|
126
134
|
#=================================================================
|
@@ -609,6 +617,61 @@ module Sphinx
|
|
609
617
|
end
|
610
618
|
alias :SetSelect :set_select
|
611
619
|
|
620
|
+
# Allows to control a number of per-query options.
|
621
|
+
#
|
622
|
+
# Supported options and respectively allowed values are:
|
623
|
+
#
|
624
|
+
# * +reverse_scan+ -- +0+ or +1+, lets you control the order in which full-scan query processes the rows.
|
625
|
+
# * +sort_method+ -- +"pq"+ (priority queue, set by default) or +"kbuffer"+
|
626
|
+
# (gives faster sorting for already pre-sorted data, e.g. index data sorted by id).
|
627
|
+
# The result set is in both cases the same; picking one option or the other
|
628
|
+
# may just improve (or worsen!) performance.
|
629
|
+
# * +boolean_simplify+ -- +false+ or +true+, enables simplifying the query to speed it up.
|
630
|
+
# * +idf+ -- either +"normalized"+ (default) or +"plain"+.
|
631
|
+
#
|
632
|
+
# @param [String] flag_name name of the option to set value for
|
633
|
+
# @param [Object] flag_value value to set
|
634
|
+
# @return [Sphinx::Client] self.
|
635
|
+
#
|
636
|
+
# @see http://sphinxsearch.com/docs/current.html#sphinxql-select
|
637
|
+
# @see http://sphinxsearch.com/docs/current.html#conf-predicted-time-costs
|
638
|
+
#
|
639
|
+
def set_query_flag(flag_name, flag_value)
|
640
|
+
raise ArgumentError, 'unknown "flag_name" argument value' unless QUERY_FLAGS.has_key?(flag_name)
|
641
|
+
|
642
|
+
flag = QUERY_FLAGS[flag_name]
|
643
|
+
values = QUERY_FLAGS[flag_name][:values]
|
644
|
+
|
645
|
+
if flag_name.to_s == 'max_predicted_time'
|
646
|
+
raise ArgumentError, "\"flag_value\" should be a positive integer for \"max_predicted_time\" flag" unless flag_value.kind_of?(Integer) and flag_value >= 0
|
647
|
+
|
648
|
+
@predictedtime = flag_value
|
649
|
+
elsif !values.include?(flag_value)
|
650
|
+
raise ArgumentError, "unknown \"flag_value\", should be one of #{values.inspect}"
|
651
|
+
end
|
652
|
+
|
653
|
+
is_set = values.respond_to?(:call) ? values.call(flag_value) : values.index(flag_value) == 1
|
654
|
+
@query_flags = set_bit(@query_flags, flag[:index], is_set)
|
655
|
+
self
|
656
|
+
end
|
657
|
+
alias :SetQueryFlag set_query_flag
|
658
|
+
|
659
|
+
def set_outer_select(orderby, offset, limit)
|
660
|
+
raise ArgumentError, '"orderby" argument must be String' unless orderby.kind_of?(String)
|
661
|
+
raise ArgumentError, '"offset" argument must be Integer' unless offset.kind_of?(Integer)
|
662
|
+
raise ArgumentError, '"limit" argument must be Integer' unless limit.kind_of?(Integer)
|
663
|
+
|
664
|
+
raise ArgumentError, '"offset" argument should be greater or equal to zero' unless offset >= 0
|
665
|
+
raise ArgumentError, '"limit" argument should be greater to zero' unless limit > 0
|
666
|
+
|
667
|
+
@outerorderby = orderby
|
668
|
+
@outeroffset = offset
|
669
|
+
@outerlimit = limit
|
670
|
+
@hasouter = true
|
671
|
+
self
|
672
|
+
end
|
673
|
+
alias :SetOuterSelect set_outer_select
|
674
|
+
|
612
675
|
#=================================================================
|
613
676
|
# Full-text search query settings
|
614
677
|
#=================================================================
|
@@ -655,17 +718,33 @@ module Sphinx
|
|
655
718
|
# matching mode at the time of this writing. Parameter must be a
|
656
719
|
# constant specifying one of the known modes.
|
657
720
|
#
|
721
|
+
# By default, in the +EXTENDED+ matching mode Sphinx computes two
|
722
|
+
# factors which contribute to the final match weight. The major
|
723
|
+
# part is a phrase proximity value between the document text and
|
724
|
+
# the query. The minor part is so-called BM25 statistical function,
|
725
|
+
# which varies from 0 to 1 depending on the keyword frequency within
|
726
|
+
# document (more occurrences yield higher weight) and within the whole
|
727
|
+
# index (more rare keywords yield higher weight).
|
728
|
+
#
|
729
|
+
# However, in some cases you'd want to compute weight differently - or
|
730
|
+
# maybe avoid computing it at all for performance reasons because you're
|
731
|
+
# sorting the result set by something else anyway. This can be accomplished
|
732
|
+
# by setting the appropriate ranking mode.
|
733
|
+
#
|
658
734
|
# You can specify ranking mode as String ("proximity_bm25", "bm25", etc),
|
659
735
|
# Symbol (:proximity_bm25, :bm25, etc), or
|
660
736
|
# Fixnum constant (SPH_RANK_PROXIMITY_BM25, SPH_RANK_BM25, etc).
|
661
737
|
#
|
662
738
|
# @param [Integer, String, Symbol] ranker ranking mode.
|
739
|
+
# @param [String] rankexpr ranking formula to use with the expression
|
740
|
+
# based ranker (+SPH_RANK_EXPR+).
|
663
741
|
# @return [Sphinx::Client] self.
|
664
742
|
#
|
665
743
|
# @example
|
666
744
|
# sphinx.set_ranking_mode(Sphinx::SPH_RANK_BM25)
|
667
745
|
# sphinx.set_ranking_mode(:bm25)
|
668
746
|
# sphinx.set_ranking_mode('bm25')
|
747
|
+
# sphinx.set_ranking_mode(:expr, 'sum(lcs*user_weight)*1000+bm25')
|
669
748
|
#
|
670
749
|
# @raise [ArgumentError] Occurred when parameters are invalid.
|
671
750
|
#
|
@@ -673,21 +752,26 @@ module Sphinx
|
|
673
752
|
# @see http://www.sphinxsearch.com/docs/current.html#api-func-setmatchmode Section 6.3.1, "SetMatchMode"
|
674
753
|
# @see http://www.sphinxsearch.com/docs/current.html#api-func-setrankingmode Section 6.3.2, "SetRankingMode"
|
675
754
|
#
|
676
|
-
def set_ranking_mode(ranker)
|
755
|
+
def set_ranking_mode(ranker, rankexpr = '')
|
677
756
|
case ranker
|
678
757
|
when String, Symbol
|
679
|
-
|
680
|
-
|
681
|
-
rescue NameError
|
758
|
+
const_name = "SPH_RANK_#{ranker.to_s.upcase}"
|
759
|
+
unless self.class.const_defined?(const_name)
|
682
760
|
raise ArgumentError, "\"ranker\" argument value \"#{ranker}\" is invalid"
|
683
761
|
end
|
762
|
+
|
763
|
+
ranker = self.class.const_get(const_name)
|
684
764
|
when Fixnum
|
685
765
|
raise ArgumentError, "\"ranker\" argument value \"#{ranker}\" is invalid" unless (SPH_RANK_PROXIMITY_BM25..SPH_RANK_SPH04).include?(ranker)
|
686
766
|
else
|
687
767
|
raise ArgumentError, '"ranker" argument must be Fixnum, String, or Symbol'
|
688
768
|
end
|
689
769
|
|
770
|
+
raise ArgumentError, '"rankexpr" argument must be String' unless rankexpr.kind_of?(String)
|
771
|
+
raise ArgumentError, '"rankexpr" should not be empty if ranker is SPH_RANK_EXPR' if ranker == SPH_RANK_EXPR and rankexpr.empty?
|
772
|
+
|
690
773
|
@ranker = ranker
|
774
|
+
@rankexpr = rankexpr
|
691
775
|
self
|
692
776
|
end
|
693
777
|
alias :SetRankingMode :set_ranking_mode
|
@@ -1259,6 +1343,22 @@ module Sphinx
|
|
1259
1343
|
end
|
1260
1344
|
alias :ResetOverrides :reset_overrides
|
1261
1345
|
|
1346
|
+
def reset_query_flag
|
1347
|
+
@query_flags = 0
|
1348
|
+
@predictedtime = 0
|
1349
|
+
self
|
1350
|
+
end
|
1351
|
+
alias :ResetQueryFlag :reset_query_flag
|
1352
|
+
|
1353
|
+
def reset_outer_select
|
1354
|
+
@outerorderby = ''
|
1355
|
+
@outeroffset = 0
|
1356
|
+
@outerlimit = 0
|
1357
|
+
@hasouter = 0
|
1358
|
+
self
|
1359
|
+
end
|
1360
|
+
alias :ResetOuterSelect :reset_outer_select
|
1361
|
+
|
1262
1362
|
# Connects to searchd server, runs given search query with
|
1263
1363
|
# current settings, obtains and returns the result set.
|
1264
1364
|
#
|
@@ -1473,7 +1573,12 @@ module Sphinx
|
|
1473
1573
|
|
1474
1574
|
# mode and limits
|
1475
1575
|
request = Request.new
|
1476
|
-
request.put_int @offset, @limit, @mode
|
1576
|
+
request.put_int @query_flags, @offset, @limit, @mode
|
1577
|
+
# ranker
|
1578
|
+
request.put_int @ranker
|
1579
|
+
request.put_string @rankexpr if @ranker == SPH_RANK_EXPR
|
1580
|
+
# sorting
|
1581
|
+
request.put_int @sort
|
1477
1582
|
request.put_string @sortby
|
1478
1583
|
# query itself
|
1479
1584
|
request.put_string query
|
@@ -1524,7 +1629,7 @@ module Sphinx
|
|
1524
1629
|
|
1525
1630
|
# per-index weights
|
1526
1631
|
request.put_int @indexweights.length
|
1527
|
-
@indexweights.each do |idx, weight|
|
1632
|
+
@indexweights.sort_by { |idx, _| idx }.each do |idx, weight|
|
1528
1633
|
request.put_string idx.to_s
|
1529
1634
|
request.put_int weight
|
1530
1635
|
end
|
@@ -1534,7 +1639,7 @@ module Sphinx
|
|
1534
1639
|
|
1535
1640
|
# per-field weights
|
1536
1641
|
request.put_int @fieldweights.length
|
1537
|
-
@fieldweights.each do |field, weight|
|
1642
|
+
@fieldweights.sort_by { |idx, _| idx }.each do |field, weight|
|
1538
1643
|
request.put_string field.to_s
|
1539
1644
|
request.put_int weight
|
1540
1645
|
end
|
@@ -1563,6 +1668,13 @@ module Sphinx
|
|
1563
1668
|
# select-list
|
1564
1669
|
request.put_string @select
|
1565
1670
|
|
1671
|
+
# max_predicted_time
|
1672
|
+
request.put_int @predictedtime if @predictedtime > 0
|
1673
|
+
|
1674
|
+
# outer select
|
1675
|
+
request.put_string @outerorderby
|
1676
|
+
request.put_int @outeroffset, @outerlimit, (@hasouter ? 1 : 0)
|
1677
|
+
|
1566
1678
|
# store request to requests array
|
1567
1679
|
@reqs << request.to_s;
|
1568
1680
|
return @reqs.length - 1
|
@@ -1611,52 +1723,49 @@ module Sphinx
|
|
1611
1723
|
|
1612
1724
|
reqs, nreqs = @reqs.join(''), @reqs.length
|
1613
1725
|
@reqs = []
|
1614
|
-
response = perform_request(:search, reqs, nreqs)
|
1726
|
+
response = perform_request(:search, reqs, [0, nreqs])
|
1615
1727
|
|
1616
1728
|
# parse response
|
1617
1729
|
(1..nreqs).map do
|
1618
|
-
result = HashWithIndifferentAccess.new(
|
1730
|
+
result = HashWithIndifferentAccess.new(:error => '', :warning => '')
|
1619
1731
|
|
1620
1732
|
# extract status
|
1621
|
-
status = result[
|
1733
|
+
status = result[:status] = response.get_int
|
1622
1734
|
if status != SEARCHD_OK
|
1623
1735
|
message = response.get_string
|
1624
1736
|
if status == SEARCHD_WARNING
|
1625
|
-
result[
|
1737
|
+
result[:warning] = message
|
1626
1738
|
else
|
1627
|
-
result[
|
1739
|
+
result[:error] = message
|
1628
1740
|
next result
|
1629
1741
|
end
|
1630
1742
|
end
|
1631
1743
|
|
1632
1744
|
# read schema
|
1633
1745
|
nfields = response.get_int
|
1634
|
-
result[
|
1746
|
+
result[:fields] = (1..nfields).map { response.get_string }
|
1635
1747
|
|
1636
1748
|
attrs_names_in_order = []
|
1637
1749
|
nattrs = response.get_int
|
1638
|
-
attrs =
|
1750
|
+
attrs = nattrs.times.inject(HashWithIndifferentAccess.new) do |hash, idx|
|
1639
1751
|
name, type = response.get_string, response.get_int
|
1640
1752
|
hash[name] = type
|
1641
1753
|
attrs_names_in_order << name
|
1642
1754
|
hash
|
1643
1755
|
end
|
1644
|
-
result[
|
1756
|
+
result[:attrs] = attrs
|
1645
1757
|
|
1646
1758
|
# read match count
|
1647
1759
|
count, id64 = response.get_ints(2)
|
1648
1760
|
|
1649
1761
|
# read matches
|
1650
|
-
result[
|
1651
|
-
doc
|
1652
|
-
|
1653
|
-
else
|
1654
|
-
[response.get_int64, response.get_int]
|
1655
|
-
end
|
1762
|
+
result[:matches] = (1..count).map do
|
1763
|
+
doc = id64 == 0 ? response.get_int : response.get_int64
|
1764
|
+
weight = response.get_int
|
1656
1765
|
|
1657
1766
|
# This is a single result put in the result['matches'] array
|
1658
|
-
match =
|
1659
|
-
match[
|
1767
|
+
match = HashWithIndifferentAccess.new(:id => doc, :weight => weight)
|
1768
|
+
match[:attrs] = attrs_names_in_order.inject(HashWithIndifferentAccess.new) do |hash, name|
|
1660
1769
|
hash[name] = case attrs[name]
|
1661
1770
|
when SPH_ATTR_BIGINT
|
1662
1771
|
# handle 64-bit ints
|
@@ -1665,28 +1774,35 @@ module Sphinx
|
|
1665
1774
|
# handle floats
|
1666
1775
|
response.get_float
|
1667
1776
|
when SPH_ATTR_STRING
|
1777
|
+
# handle string
|
1668
1778
|
response.get_string
|
1779
|
+
when SPH_ATTR_FACTORS
|
1780
|
+
# ???
|
1781
|
+
response.get_int
|
1782
|
+
when SPH_ATTR_MULTI
|
1783
|
+
# handle array of integers
|
1784
|
+
val = response.get_int
|
1785
|
+
response.get_ints(val) if val > 0
|
1786
|
+
when SPH_ATTR_MULTI64
|
1787
|
+
# handle array of 64-bit integers
|
1788
|
+
val = response.get_int
|
1789
|
+
(val / 2).times.map { response.get_int64 }
|
1669
1790
|
else
|
1670
1791
|
# handle everything else as unsigned ints
|
1671
|
-
|
1672
|
-
if (attrs[name] & SPH_ATTR_MULTI) != 0
|
1673
|
-
(1..val).map { response.get_int }
|
1674
|
-
else
|
1675
|
-
val
|
1676
|
-
end
|
1792
|
+
response.get_int
|
1677
1793
|
end
|
1678
1794
|
hash
|
1679
1795
|
end
|
1680
1796
|
match
|
1681
1797
|
end
|
1682
|
-
result[
|
1683
|
-
result[
|
1798
|
+
result[:total], result[:total_found], msecs = response.get_ints(3)
|
1799
|
+
result[:time] = '%.3f' % (msecs / 1000.0)
|
1684
1800
|
|
1685
1801
|
nwords = response.get_int
|
1686
|
-
result[
|
1802
|
+
result[:words] = nwords.times.inject({}) do |hash, idx|
|
1687
1803
|
word = response.get_string
|
1688
1804
|
docs, hits = response.get_ints(2)
|
1689
|
-
hash[word] =
|
1805
|
+
hash[word] = HashWithIndifferentAccess.new(:docs => docs, :hits => hits)
|
1690
1806
|
hash
|
1691
1807
|
end
|
1692
1808
|
|
@@ -1761,27 +1877,42 @@ module Sphinx
|
|
1761
1877
|
|
1762
1878
|
# fixup options
|
1763
1879
|
opts = HashWithIndifferentAccess.new(
|
1764
|
-
|
1765
|
-
|
1766
|
-
|
1767
|
-
|
1768
|
-
|
1769
|
-
|
1770
|
-
|
1771
|
-
|
1772
|
-
|
1773
|
-
|
1880
|
+
:before_match => '<b>',
|
1881
|
+
:after_match => '</b>',
|
1882
|
+
:chunk_separator => ' ... ',
|
1883
|
+
:limit => 256,
|
1884
|
+
:limit_passages => 0,
|
1885
|
+
:limit_words => 0,
|
1886
|
+
:around => 5,
|
1887
|
+
:exact_phrase => false,
|
1888
|
+
:single_passage => false,
|
1889
|
+
:use_boundaries => false,
|
1890
|
+
:weight_order => false,
|
1891
|
+
:query_mode => false,
|
1892
|
+
:force_all_words => false,
|
1893
|
+
:start_passage_id => 1,
|
1894
|
+
:load_files => false,
|
1895
|
+
:html_strip_mode => 'index',
|
1896
|
+
:allow_empty => false,
|
1897
|
+
:passage_boundary => 'none',
|
1898
|
+
:emit_zones => false,
|
1899
|
+
:load_files_scattered => false
|
1774
1900
|
).update(opts)
|
1775
1901
|
|
1776
1902
|
# build request
|
1777
1903
|
|
1778
|
-
# v.1.
|
1779
|
-
flags
|
1780
|
-
flags |= 2
|
1781
|
-
flags |= 4
|
1782
|
-
flags |= 8
|
1783
|
-
flags |= 16
|
1784
|
-
flags |= 32
|
1904
|
+
# v.1.2 req
|
1905
|
+
flags = 1
|
1906
|
+
flags |= 2 if opts[:exact_phrase]
|
1907
|
+
flags |= 4 if opts[:single_passage]
|
1908
|
+
flags |= 8 if opts[:use_boundaries]
|
1909
|
+
flags |= 16 if opts[:weight_order]
|
1910
|
+
flags |= 32 if opts[:query_mode]
|
1911
|
+
flags |= 64 if opts[:force_all_words]
|
1912
|
+
flags |= 128 if opts[:load_files]
|
1913
|
+
flags |= 256 if opts[:allow_empty]
|
1914
|
+
flags |= 512 if opts[:emit_zones]
|
1915
|
+
flags |= 1024 if opts[:load_files_scattered]
|
1785
1916
|
|
1786
1917
|
request = Request.new
|
1787
1918
|
request.put_int 0, flags # mode=0, flags=1 (remove spaces)
|
@@ -1791,10 +1922,10 @@ module Sphinx
|
|
1791
1922
|
request.put_string words
|
1792
1923
|
|
1793
1924
|
# options
|
1794
|
-
request.put_string opts[
|
1795
|
-
request.
|
1796
|
-
request.
|
1797
|
-
request.
|
1925
|
+
request.put_string opts[:before_match], opts[:after_match], opts[:chunk_separator]
|
1926
|
+
request.put_int opts[:limit].to_i, opts[:around].to_i
|
1927
|
+
request.put_int opts[:limit_passages].to_i, opts[:limit_words].to_i, opts[:start_passage_id].to_i
|
1928
|
+
request.put_string opts[:html_strip_mode], opts[:passage_boundary]
|
1798
1929
|
|
1799
1930
|
# documents
|
1800
1931
|
request.put_int docs.size
|
@@ -1918,10 +2049,11 @@ module Sphinx
|
|
1918
2049
|
#
|
1919
2050
|
# @see http://www.sphinxsearch.com/docs/current.html#api-func-updateatttributes Section 6.7.2, "UpdateAttributes"
|
1920
2051
|
#
|
1921
|
-
def update_attributes(index, attrs, values, mva = false)
|
2052
|
+
def update_attributes(index, attrs, values, mva = false, ignore_non_existent = false)
|
1922
2053
|
# verify everything
|
1923
2054
|
raise ArgumentError, '"index" argument must be String' unless index.kind_of?(String) or index.kind_of?(Symbol)
|
1924
2055
|
raise ArgumentError, '"mva" argument must be Boolean' unless mva.kind_of?(TrueClass) or mva.kind_of?(FalseClass)
|
2056
|
+
raise ArgumentError, '"ignore_non_existent" argument must be Boolean' unless ignore_non_existent.kind_of?(TrueClass) or ignore_non_existent.kind_of?(FalseClass)
|
1925
2057
|
|
1926
2058
|
raise ArgumentError, '"attrs" argument must be Array' unless attrs.kind_of?(Array)
|
1927
2059
|
attrs.each do |attr|
|
@@ -1950,6 +2082,7 @@ module Sphinx
|
|
1950
2082
|
request.put_string index
|
1951
2083
|
|
1952
2084
|
request.put_int attrs.length
|
2085
|
+
request.put_int ignore_non_existent ? 1 : 0
|
1953
2086
|
for attr in attrs
|
1954
2087
|
request.put_string attr
|
1955
2088
|
request.put_int mva ? 1 : 0
|
@@ -2051,7 +2184,7 @@ module Sphinx
|
|
2051
2184
|
# @example
|
2052
2185
|
# sphinx.flush_attrs
|
2053
2186
|
#
|
2054
|
-
def
|
2187
|
+
def flush_attributes
|
2055
2188
|
request = Request.new
|
2056
2189
|
response = perform_request(:flushattrs, request)
|
2057
2190
|
|
@@ -2059,10 +2192,13 @@ module Sphinx
|
|
2059
2192
|
begin
|
2060
2193
|
response.get_int
|
2061
2194
|
rescue EOFError
|
2195
|
+
@error = 'unexpected response length'
|
2062
2196
|
-1
|
2063
2197
|
end
|
2064
2198
|
end
|
2065
|
-
alias :
|
2199
|
+
alias :FlushAttributes :flush_attributes
|
2200
|
+
alias :FlushAttrs :flush_attributes
|
2201
|
+
alias :flush_attrs :flush_attributes
|
2066
2202
|
|
2067
2203
|
#=================================================================
|
2068
2204
|
# Persistent connections
|
@@ -2152,7 +2288,7 @@ module Sphinx
|
|
2152
2288
|
# <tt>:update</tt>, <tt>:keywords</tt>, <tt>:persist</tt>, <tt>:status</tt>,
|
2153
2289
|
# <tt>:query</tt>, <tt>:flushattrs</tt>. See <tt>SEARCHD_COMMAND_*</tt> for details).
|
2154
2290
|
# @param [Sphinx::Request] request contains request body.
|
2155
|
-
# @param [Integer] additional additional integer data to be placed between header and body.
|
2291
|
+
# @param [Integer, Array] additional additional integer or array of integers data to be placed between header and body.
|
2156
2292
|
# @param [Sphinx::Server] server where perform request on. This is special
|
2157
2293
|
# parameter for internal usage. If specified, request will be performed
|
2158
2294
|
# on specified server, and it will try to establish connection to this
|
@@ -2182,22 +2318,23 @@ module Sphinx
|
|
2182
2318
|
attempts = nil
|
2183
2319
|
end
|
2184
2320
|
|
2185
|
-
with_server(server, attempts) do |
|
2186
|
-
logger.info { "[sphinx] #{command} on server #{
|
2321
|
+
with_server(server, attempts) do |srv|
|
2322
|
+
logger.info { "[sphinx] #{command} on server #{srv}" } if logger
|
2187
2323
|
|
2188
2324
|
cmd = command.to_s.upcase
|
2189
2325
|
command_id = Sphinx::Client.const_get("SEARCHD_COMMAND_#{cmd}")
|
2190
2326
|
command_ver = Sphinx::Client.const_get("VER_COMMAND_#{cmd}")
|
2191
2327
|
|
2192
|
-
with_socket(
|
2193
|
-
|
2328
|
+
with_socket(srv) do |socket|
|
2329
|
+
additional = Array(additional)
|
2330
|
+
len = request.to_s.length + (additional.size * 4)
|
2194
2331
|
header = [command_id, command_ver, len].pack('nnN')
|
2195
|
-
header <<
|
2332
|
+
header << additional.pack('N' * additional.size) unless additional.empty?
|
2196
2333
|
|
2197
2334
|
socket.write(header + request.to_s)
|
2198
2335
|
|
2199
2336
|
if block_given?
|
2200
|
-
yield
|
2337
|
+
yield srv, socket
|
2201
2338
|
else
|
2202
2339
|
parse_response(socket, command_ver)
|
2203
2340
|
end
|
@@ -2431,6 +2568,22 @@ module Sphinx
|
|
2431
2568
|
end
|
2432
2569
|
end
|
2433
2570
|
|
2571
|
+
# Sets or resets given bit in a bitset.
|
2572
|
+
#
|
2573
|
+
# @param [Integer] bitset integer value to set bit in.
|
2574
|
+
# @param [Integer] index integer offset of the bit to set.
|
2575
|
+
# @param [Boolean,Integer] value value to set bit into (+true+, +false+, +0+, or +1+).
|
2576
|
+
#
|
2577
|
+
def set_bit(bitset, index, value)
|
2578
|
+
bit = 1 << index
|
2579
|
+
if value == true || value == 1
|
2580
|
+
bitset |= bit
|
2581
|
+
elsif bitset & bit > 0
|
2582
|
+
bitset ^= bit
|
2583
|
+
end
|
2584
|
+
bitset
|
2585
|
+
end
|
2586
|
+
|
2434
2587
|
# Enables ability to skip +set_+ prefix for methods inside {#query} block.
|
2435
2588
|
#
|
2436
2589
|
# @example
|