riddle 1.4.0 → 1.5.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +6 -0
- data/.travis.yml +16 -0
- data/Gemfile +6 -0
- data/HISTORY +45 -0
- data/LICENCE +20 -0
- data/README.textile +5 -3
- data/Rakefile +23 -0
- data/lib/riddle.rb +1 -0
- data/lib/riddle/0.9.9/configuration/searchd.rb +10 -8
- data/lib/riddle/auto_version.rb +2 -2
- data/lib/riddle/client.rb +117 -118
- data/lib/riddle/configuration.rb +6 -6
- data/lib/riddle/configuration/distributed_index.rb +16 -16
- data/lib/riddle/configuration/sql_source.rb +5 -5
- data/lib/riddle/controller.rb +28 -25
- data/lib/riddle/query.rb +31 -20
- data/lib/riddle/query/select.rb +69 -8
- data/lib/riddle/version.rb +3 -0
- data/riddle.gemspec +25 -0
- data/spec/fixtures/.gitignore +2 -0
- data/spec/fixtures/data/0.9.9/anchor.bin +0 -0
- data/spec/fixtures/data/0.9.9/any.bin +0 -0
- data/spec/fixtures/data/0.9.9/boolean.bin +0 -0
- data/spec/fixtures/data/0.9.9/comment.bin +0 -0
- data/spec/fixtures/data/0.9.9/distinct.bin +0 -0
- data/spec/fixtures/data/0.9.9/field_weights.bin +0 -0
- data/spec/fixtures/data/0.9.9/filter.bin +0 -0
- data/spec/fixtures/data/0.9.9/filter_array.bin +0 -0
- data/spec/fixtures/data/0.9.9/filter_array_exclude.bin +0 -0
- data/spec/fixtures/data/0.9.9/filter_boolean.bin +0 -0
- data/spec/fixtures/data/0.9.9/filter_floats.bin +0 -0
- data/spec/fixtures/data/0.9.9/filter_floats_exclude.bin +0 -0
- data/spec/fixtures/data/0.9.9/filter_range.bin +0 -0
- data/spec/fixtures/data/0.9.9/filter_range_exclude.bin +0 -0
- data/spec/fixtures/data/0.9.9/group.bin +0 -0
- data/spec/fixtures/data/0.9.9/index.bin +0 -0
- data/spec/fixtures/data/0.9.9/index_weights.bin +0 -0
- data/spec/fixtures/data/0.9.9/keywords_with_hits.bin +0 -0
- data/spec/fixtures/data/0.9.9/keywords_without_hits.bin +0 -0
- data/spec/fixtures/data/0.9.9/overrides.bin +0 -0
- data/spec/fixtures/data/0.9.9/phrase.bin +0 -0
- data/spec/fixtures/data/0.9.9/rank_mode.bin +0 -0
- data/spec/fixtures/data/0.9.9/select.bin +0 -0
- data/spec/fixtures/data/0.9.9/simple.bin +0 -0
- data/spec/fixtures/data/0.9.9/sort.bin +0 -0
- data/spec/fixtures/data/0.9.9/update_simple.bin +0 -0
- data/spec/fixtures/data/0.9.9/weights.bin +0 -0
- data/spec/fixtures/data/1.10/anchor.bin +0 -0
- data/spec/fixtures/data/1.10/any.bin +0 -0
- data/spec/fixtures/data/1.10/boolean.bin +0 -0
- data/spec/fixtures/data/1.10/comment.bin +0 -0
- data/spec/fixtures/data/1.10/distinct.bin +0 -0
- data/spec/fixtures/data/1.10/field_weights.bin +0 -0
- data/spec/fixtures/data/1.10/filter.bin +0 -0
- data/spec/fixtures/data/1.10/filter_array.bin +0 -0
- data/spec/fixtures/data/1.10/filter_array_exclude.bin +0 -0
- data/spec/fixtures/data/1.10/filter_boolean.bin +0 -0
- data/spec/fixtures/data/1.10/filter_floats.bin +0 -0
- data/spec/fixtures/data/1.10/filter_floats_exclude.bin +0 -0
- data/spec/fixtures/data/1.10/filter_range.bin +0 -0
- data/spec/fixtures/data/1.10/filter_range_exclude.bin +0 -0
- data/spec/fixtures/data/1.10/group.bin +0 -0
- data/spec/fixtures/data/1.10/index.bin +0 -0
- data/spec/fixtures/data/1.10/index_weights.bin +0 -0
- data/spec/fixtures/data/1.10/keywords_with_hits.bin +0 -0
- data/spec/fixtures/data/1.10/keywords_without_hits.bin +0 -0
- data/spec/fixtures/data/1.10/overrides.bin +0 -0
- data/spec/fixtures/data/1.10/phrase.bin +0 -0
- data/spec/fixtures/data/1.10/rank_mode.bin +0 -0
- data/spec/fixtures/data/1.10/select.bin +0 -0
- data/spec/fixtures/data/1.10/simple.bin +0 -0
- data/spec/fixtures/data/1.10/sort.bin +0 -0
- data/spec/fixtures/data/1.10/update_simple.bin +0 -0
- data/spec/fixtures/data/1.10/weights.bin +0 -0
- data/spec/fixtures/data/2.0.1/anchor.bin +0 -0
- data/spec/fixtures/data/2.0.1/any.bin +0 -0
- data/spec/fixtures/data/2.0.1/boolean.bin +0 -0
- data/spec/fixtures/data/2.0.1/comment.bin +0 -0
- data/spec/fixtures/data/2.0.1/distinct.bin +0 -0
- data/spec/fixtures/data/2.0.1/field_weights.bin +0 -0
- data/spec/fixtures/data/2.0.1/filter.bin +0 -0
- data/spec/fixtures/data/2.0.1/filter_array.bin +0 -0
- data/spec/fixtures/data/2.0.1/filter_array_exclude.bin +0 -0
- data/spec/fixtures/data/2.0.1/filter_boolean.bin +0 -0
- data/spec/fixtures/data/2.0.1/filter_floats.bin +0 -0
- data/spec/fixtures/data/2.0.1/filter_floats_exclude.bin +0 -0
- data/spec/fixtures/data/2.0.1/filter_range.bin +0 -0
- data/spec/fixtures/data/2.0.1/filter_range_exclude.bin +0 -0
- data/spec/fixtures/data/2.0.1/group.bin +0 -0
- data/spec/fixtures/data/2.0.1/index.bin +0 -0
- data/spec/fixtures/data/2.0.1/index_weights.bin +0 -0
- data/spec/fixtures/data/2.0.1/keywords_with_hits.bin +0 -0
- data/spec/fixtures/data/2.0.1/keywords_without_hits.bin +0 -0
- data/spec/fixtures/data/2.0.1/overrides.bin +0 -0
- data/spec/fixtures/data/2.0.1/phrase.bin +0 -0
- data/spec/fixtures/data/2.0.1/rank_mode.bin +0 -0
- data/spec/fixtures/data/2.0.1/select.bin +0 -0
- data/spec/fixtures/data/2.0.1/simple.bin +0 -0
- data/spec/fixtures/data/2.0.1/sort.bin +0 -0
- data/spec/fixtures/data/2.0.1/update_simple.bin +0 -0
- data/spec/fixtures/data/2.0.1/weights.bin +0 -0
- data/spec/fixtures/data_generator.0.9.8.php +208 -0
- data/spec/fixtures/data_generator.0.9.9.php +5 -0
- data/spec/fixtures/data_generator.1.10.php +5 -0
- data/spec/fixtures/data_generator.2.0.1.php +5 -0
- data/spec/fixtures/data_generator.php +223 -0
- data/spec/fixtures/sphinxapi.0.9.8.php +1228 -0
- data/spec/fixtures/sphinxapi.0.9.9.php +1646 -0
- data/spec/fixtures/sphinxapi.1.10.php +1728 -0
- data/spec/fixtures/sphinxapi.2.0.1.php +1731 -0
- data/spec/fixtures/sql/conf.example.yml +3 -0
- data/spec/fixtures/sql/data.sql +25000 -0
- data/spec/fixtures/sql/data.tsv +25000 -0
- data/spec/fixtures/sql/structure.sql +16 -0
- data/spec/functional/connection_spec.rb +10 -12
- data/spec/functional/excerpt_spec.rb +1 -1
- data/spec/functional/keywords_spec.rb +1 -1
- data/spec/functional/persistance_spec.rb +1 -1
- data/spec/functional/search_spec.rb +1 -1
- data/spec/functional/status_spec.rb +1 -1
- data/spec/functional/update_spec.rb +1 -1
- data/spec/riddle/auto_version_spec.rb +18 -10
- data/spec/riddle/query/select_spec.rb +78 -14
- data/spec/riddle/query_spec.rb +5 -3
- data/spec/spec_helper.rb +13 -15
- data/spec/support/binary_fixtures.rb +18 -0
- data/spec/support/sphinx.rb +135 -0
- data/spec/unit/client_spec.rb +150 -142
- data/spec/unit/configuration/distributed_index_spec.rb +15 -15
- data/spec/unit/configuration/searchd_spec.rb +28 -3
- data/spec/unit/configuration_spec.rb +6 -6
- metadata +254 -68
- data/spec/sphinx_helper.rb +0 -96
data/.gitignore
ADDED
data/.travis.yml
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
rvm:
|
2
|
+
- 1.8.6
|
3
|
+
- 1.8.7
|
4
|
+
- 1.9.2
|
5
|
+
- 1.9.3
|
6
|
+
- rbx
|
7
|
+
- rbx-2.0
|
8
|
+
- ree
|
9
|
+
- jruby
|
10
|
+
- ruby-head
|
11
|
+
env:
|
12
|
+
- SPHINX_BIN=/usr/local/sphinx-0.9.9/bin SPHINX_VERSION=0.9.9
|
13
|
+
- SPHINX_BIN=/usr/local/sphinx-1.10/bin SPHINX_VERSION=1.10
|
14
|
+
- SPHINX_BIN=/usr/local/sphinx-2.0.1/bin SPHINX_VERSION=2.0.1
|
15
|
+
before_script: killall searchd; echo ''
|
16
|
+
after_script: killall searchd; echo ''
|
data/Gemfile
ADDED
data/HISTORY
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
1.5.0 - November 4th 2011
|
2
|
+
- Handle exclusive filters in SphinxQL SELECT commands.
|
3
|
+
- Allow for native Ruby objects in SphinxQL UPDATE commands.
|
4
|
+
- Handle options of hashes in SphinxQL SELECT commands.
|
5
|
+
- Allow for SphinxQL select clauses.
|
6
|
+
- Improving SphinxQL filter handling of native Ruby objects.
|
7
|
+
- Switch plural index references from indexes to indices, to distinguish beside indexes (the action).
|
8
|
+
- Rescue against timeouts and connection resets.
|
9
|
+
- Fixing reference to TCPSocket.
|
10
|
+
- Handle port numbers as integers for listen setting (Ngan Pham).
|
11
|
+
- Provide the option to start searchd with the nodetach flag (Aaron Gilbralter).
|
12
|
+
- Don't shuffle servers (if there's more than one) - let developers (or Thinking Sphinx) manage that (Ngan Pham).
|
13
|
+
|
14
|
+
1.4.0 - August 2nd 2011
|
15
|
+
- Checking against both Windows platforms for Ruby (Paul Gibler)
|
16
|
+
- Encoding improvements (Alexey Artamonov)
|
17
|
+
- More Rubyish syntax (James Cook)
|
18
|
+
- Handling Ruby encodings (James Cook)
|
19
|
+
- Coreseek support (saberma)
|
20
|
+
- Section restructure for better inheritance (Alexey Artamonov)
|
21
|
+
- MySQL41 connection support
|
22
|
+
- requiring 'thread' for Mutex use
|
23
|
+
|
24
|
+
1.3.3 - May 25th 2011
|
25
|
+
- Using MySQL2 library for SphinxQL interface
|
26
|
+
- Adding Sphinx 2.0.x settings
|
27
|
+
- SphinxQL support
|
28
|
+
- Speed improvements for hash lookups (Enrico Thierbach)
|
29
|
+
- Handle race conditions of segfaults while returning responses (Jason Lambert)
|
30
|
+
- 2.0.x support
|
31
|
+
|
32
|
+
1.3.2 - May 12th 2011
|
33
|
+
- client_key support
|
34
|
+
|
35
|
+
1.3.1 - May 9th 2011
|
36
|
+
- Don't output warnings or exit when version isn't detected - presume Thinking Sphinx will handle that.
|
37
|
+
- Confirm configuration file exists before attempting to start/stop Sphinx.
|
38
|
+
- Use a Mutex instead of the current Thread.
|
39
|
+
|
40
|
+
1.3.0 - May 7th 2011
|
41
|
+
- Attempts at untested 2.0.x and client_key support
|
42
|
+
- Using Bundler, MySQL2 and Ruby 1.9.2 in development
|
43
|
+
- Allow for Sphinx versions compiled from source and SVN (Greg Weber)
|
44
|
+
|
45
|
+
1.2.2 - December 22nd 2011
|
data/LICENCE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2008-2010 Pat Allan
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.textile
CHANGED
@@ -22,12 +22,12 @@ To get started, just instantiate a Client object:
|
|
22
22
|
|
23
23
|
client = Riddle::Client.new # defaults to localhost and port 3312
|
24
24
|
client = Riddle::Client.new "sphinxserver.domain.tld", 3333 # custom settings
|
25
|
-
|
25
|
+
|
26
26
|
And then set the parameters to what you want, before running a query:
|
27
27
|
|
28
28
|
client.match_mode = :extended
|
29
29
|
client.query "Pat Allan @state Victoria"
|
30
|
-
|
30
|
+
|
31
31
|
The results from a query are similar to the other clients - but here's the details. It's a hash with
|
32
32
|
the following keys:
|
33
33
|
|
@@ -53,7 +53,7 @@ documents. The key @:attributes@ will return a hash of attribute name and type p
|
|
53
53
|
documents and hits for each, along the lines of:
|
54
54
|
|
55
55
|
results[:words]["Pat"] #=> {:docs => 12, :hits => 15}
|
56
|
-
|
56
|
+
|
57
57
|
@:total@, @:total_found@ and @:time@ return the number of matches available, the
|
58
58
|
total number of matches (which may be greater than the maximum available), and the time in milliseconds
|
59
59
|
that the query took to run.
|
@@ -91,3 +91,5 @@ Thanks to the following people who have contributed to Riddle in some shape or f
|
|
91
91
|
* James Cook
|
92
92
|
* Alexey Artamonov
|
93
93
|
* Paul Gibler
|
94
|
+
* Ngan Pham
|
95
|
+
* Aaron Gilbralter
|
data/Rakefile
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
|
4
|
+
Bundler::GemHelper.install_tasks
|
5
|
+
Bundler.require :default, :development
|
6
|
+
|
7
|
+
require 'rspec/core/rake_task'
|
8
|
+
|
9
|
+
RSpec::Core::RakeTask.new
|
10
|
+
|
11
|
+
RSpec::Core::RakeTask.new(:rcov) do |spec|
|
12
|
+
spec.rcov_opts = ['--exclude', 'spec', '--exclude', 'gems']
|
13
|
+
spec.rcov = true
|
14
|
+
end
|
15
|
+
|
16
|
+
YARD::Rake::YardocTask.new
|
17
|
+
|
18
|
+
task :default => :spec
|
19
|
+
|
20
|
+
task :fixtures do
|
21
|
+
require './spec/spec_helper'
|
22
|
+
BinaryFixtures.build_fixtures
|
23
|
+
end
|
data/lib/riddle.rb
CHANGED
@@ -4,29 +4,31 @@ module Riddle
|
|
4
4
|
def valid?
|
5
5
|
set_listen
|
6
6
|
clear_deprecated
|
7
|
-
|
7
|
+
|
8
8
|
!( @listen.nil? || @listen.empty? || @pid_file.nil? )
|
9
9
|
end
|
10
|
-
|
10
|
+
|
11
11
|
private
|
12
|
-
|
12
|
+
|
13
13
|
def set_listen
|
14
|
+
@listen = @listen.to_s if @listen.is_a?(Fixnum)
|
15
|
+
|
14
16
|
return unless @listen.nil? || @listen.empty?
|
15
|
-
|
17
|
+
|
16
18
|
@listen = []
|
17
19
|
@listen << @port.to_s if @port
|
18
20
|
@listen << "9306:mysql41" if @mysql41.is_a?(TrueClass)
|
19
21
|
@listen << "#{@mysql41}:mysql41" if @mysql41.is_a?(Fixnum)
|
20
|
-
|
22
|
+
|
21
23
|
@listen.each { |l| l.insert(0, "#{@address}:") } if @address
|
22
24
|
end
|
23
|
-
|
25
|
+
|
24
26
|
def clear_deprecated
|
25
27
|
return if @listen.nil?
|
26
|
-
|
28
|
+
|
27
29
|
@address = nil
|
28
30
|
@port = nil
|
29
31
|
end
|
30
32
|
end
|
31
33
|
end
|
32
|
-
end
|
34
|
+
end
|
data/lib/riddle/auto_version.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
class Riddle::AutoVersion
|
2
2
|
def self.configure
|
3
3
|
controller = Riddle::Controller.new nil, ''
|
4
|
-
version = controller.sphinx_version
|
5
|
-
|
4
|
+
version = ENV['SPHINX_VERSION'] || controller.sphinx_version
|
5
|
+
|
6
6
|
case version
|
7
7
|
when '0.9.8', '0.9.9'
|
8
8
|
require "riddle/#{version}"
|
data/lib/riddle/client.rb
CHANGED
@@ -6,7 +6,7 @@ module Riddle
|
|
6
6
|
class VersionError < StandardError; end
|
7
7
|
class ResponseError < StandardError; end
|
8
8
|
class OutOfBoundsError < StandardError; end
|
9
|
-
|
9
|
+
|
10
10
|
# This class was heavily based on the existing Client API by Dmytro Shteflyuk
|
11
11
|
# and Alexy Kovyrin. Their code worked fine, I just wanted something a bit
|
12
12
|
# more Ruby-ish (ie. lowercase and underscored method names). I also have
|
@@ -40,7 +40,7 @@ module Riddle
|
|
40
40
|
:query => 6, # SEARCHD_COMMAND_QUERY
|
41
41
|
:flushattrs => 7 # SEARCHD_COMMAND_FLUSHATTRS
|
42
42
|
}
|
43
|
-
|
43
|
+
|
44
44
|
Versions = {
|
45
45
|
:search => 0x113, # VER_COMMAND_SEARCH
|
46
46
|
:excerpt => 0x100, # VER_COMMAND_EXCERPT
|
@@ -50,14 +50,14 @@ module Riddle
|
|
50
50
|
:query => 0x100, # VER_COMMAND_QUERY
|
51
51
|
:flushattrs => 0x100 # VER_COMMAND_FLUSHATTRS
|
52
52
|
}
|
53
|
-
|
53
|
+
|
54
54
|
Statuses = {
|
55
55
|
:ok => 0, # SEARCHD_OK
|
56
56
|
:error => 1, # SEARCHD_ERROR
|
57
57
|
:retry => 2, # SEARCHD_RETRY
|
58
58
|
:warning => 3 # SEARCHD_WARNING
|
59
59
|
}
|
60
|
-
|
60
|
+
|
61
61
|
MatchModes = {
|
62
62
|
:all => 0, # SPH_MATCH_ALL
|
63
63
|
:any => 1, # SPH_MATCH_ANY
|
@@ -67,7 +67,7 @@ module Riddle
|
|
67
67
|
:fullscan => 5, # SPH_MATCH_FULLSCAN
|
68
68
|
:extended2 => 6 # SPH_MATCH_EXTENDED2
|
69
69
|
}
|
70
|
-
|
70
|
+
|
71
71
|
RankModes = {
|
72
72
|
:proximity_bm25 => 0, # SPH_RANK_PROXIMITY_BM25
|
73
73
|
:bm25 => 1, # SPH_RANK_BM25
|
@@ -79,7 +79,7 @@ module Riddle
|
|
79
79
|
:sph04 => 7, # SPH_RANK_SPH04
|
80
80
|
:total => 8 # SPH_RANK_TOTAL
|
81
81
|
}
|
82
|
-
|
82
|
+
|
83
83
|
SortModes = {
|
84
84
|
:relevance => 0, # SPH_SORT_RELEVANCE
|
85
85
|
:attr_desc => 1, # SPH_SORT_ATTR_DESC
|
@@ -88,7 +88,7 @@ module Riddle
|
|
88
88
|
:extended => 4, # SPH_SORT_EXTENDED
|
89
89
|
:expr => 5 # SPH_SORT_EXPR
|
90
90
|
}
|
91
|
-
|
91
|
+
|
92
92
|
AttributeTypes = {
|
93
93
|
:integer => 1, # SPH_ATTR_INTEGER
|
94
94
|
:timestamp => 2, # SPH_ATTR_TIMESTAMP
|
@@ -99,7 +99,7 @@ module Riddle
|
|
99
99
|
:string => 7, # SPH_ATTR_STRING
|
100
100
|
:multi => 0x40000000 # SPH_ATTR_MULTI
|
101
101
|
}
|
102
|
-
|
102
|
+
|
103
103
|
GroupFunctions = {
|
104
104
|
:day => 0, # SPH_GROUPBY_DAY
|
105
105
|
:week => 1, # SPH_GROUPBY_WEEK
|
@@ -108,13 +108,13 @@ module Riddle
|
|
108
108
|
:attr => 4, # SPH_GROUPBY_ATTR
|
109
109
|
:attrpair => 5 # SPH_GROUPBY_ATTRPAIR
|
110
110
|
}
|
111
|
-
|
111
|
+
|
112
112
|
FilterTypes = {
|
113
113
|
:values => 0, # SPH_FILTER_VALUES
|
114
114
|
:range => 1, # SPH_FILTER_RANGE
|
115
115
|
:float_range => 2 # SPH_FILTER_FLOATRANGE
|
116
116
|
}
|
117
|
-
|
117
|
+
|
118
118
|
attr_accessor :servers, :port, :offset, :limit, :max_matches,
|
119
119
|
:match_mode, :sort_mode, :sort_by, :weights, :id_range, :filters,
|
120
120
|
:group_by, :group_function, :group_clause, :group_distinct, :cut_off,
|
@@ -122,9 +122,9 @@ module Riddle
|
|
122
122
|
:max_query_time, :field_weights, :timeout, :overrides, :select,
|
123
123
|
:connection, :key
|
124
124
|
attr_reader :queue
|
125
|
-
|
125
|
+
|
126
126
|
@@connection = nil
|
127
|
-
|
127
|
+
|
128
128
|
def self.connection=(value)
|
129
129
|
Riddle.mutex.synchronize do
|
130
130
|
@@connection = value
|
@@ -134,24 +134,23 @@ module Riddle
|
|
134
134
|
def self.connection
|
135
135
|
@@connection
|
136
136
|
end
|
137
|
-
|
137
|
+
|
138
138
|
# Can instantiate with a specific server and port - otherwise it assumes
|
139
139
|
# defaults of localhost and 3312 respectively. All other settings can be
|
140
140
|
# accessed and changed via the attribute accessors.
|
141
141
|
def initialize(servers = nil, port = nil, key = nil)
|
142
142
|
Riddle.version_warning
|
143
143
|
|
144
|
-
servers = Array(servers || "localhost")
|
145
|
-
@
|
146
|
-
@port = port || 9312
|
144
|
+
@servers = Array(servers || "localhost")
|
145
|
+
@port = port || 9312
|
147
146
|
@socket = nil
|
148
147
|
@key = key
|
149
|
-
|
148
|
+
|
150
149
|
reset
|
151
|
-
|
150
|
+
|
152
151
|
@queue = []
|
153
152
|
end
|
154
|
-
|
153
|
+
|
155
154
|
# Reset attributes and settings to defaults.
|
156
155
|
def reset
|
157
156
|
# defaults
|
@@ -182,7 +181,7 @@ module Riddle
|
|
182
181
|
@overrides = {}
|
183
182
|
@select = "*"
|
184
183
|
end
|
185
|
-
|
184
|
+
|
186
185
|
# The searchd server to query. Servers are removed from @server after a
|
187
186
|
# Timeout::Error is hit to allow for fail-over.
|
188
187
|
def server
|
@@ -213,18 +212,18 @@ module Riddle
|
|
213
212
|
:longitude => long
|
214
213
|
}
|
215
214
|
end
|
216
|
-
|
215
|
+
|
217
216
|
# Append a query to the queue. This uses the same parameters as the query
|
218
217
|
# method.
|
219
218
|
def append_query(search, index = '*', comments = '')
|
220
219
|
@queue << query_message(search, index, comments)
|
221
220
|
end
|
222
|
-
|
221
|
+
|
223
222
|
# Run all the queries currently in the queue. This will return an array of
|
224
223
|
# results hashes.
|
225
224
|
def run
|
226
225
|
response = Response.new request(:search, @queue)
|
227
|
-
|
226
|
+
|
228
227
|
results = @queue.collect do
|
229
228
|
result = {
|
230
229
|
:matches => [],
|
@@ -242,7 +241,7 @@ module Riddle
|
|
242
241
|
result[:error] = response.next
|
243
242
|
next result
|
244
243
|
end
|
245
|
-
|
244
|
+
|
246
245
|
result[:fields] = response.next_array
|
247
246
|
|
248
247
|
attributes = response.next_int
|
@@ -259,7 +258,7 @@ module Riddle
|
|
259
258
|
|
260
259
|
matches = response.next_int
|
261
260
|
is_64_bit = response.next_int
|
262
|
-
|
261
|
+
|
263
262
|
result[:matches] = (0...matches).map do |i|
|
264
263
|
doc = is_64_bit > 0 ? response.next_64bit_int : response.next_int
|
265
264
|
weight = response.next_int
|
@@ -269,7 +268,7 @@ module Riddle
|
|
269
268
|
result_attribute_names_and_types.each do |attr, type|
|
270
269
|
current_match_attributes[attr] = attribute_from_type(type, response)
|
271
270
|
end
|
272
|
-
|
271
|
+
|
273
272
|
{:doc => doc, :weight => weight, :index => i, :attributes => current_match_attributes}
|
274
273
|
end
|
275
274
|
|
@@ -287,17 +286,17 @@ module Riddle
|
|
287
286
|
|
288
287
|
result
|
289
288
|
end
|
290
|
-
|
289
|
+
|
291
290
|
@queue.clear
|
292
291
|
results
|
293
292
|
end
|
294
|
-
|
295
|
-
# Query the Sphinx daemon - defaulting to all
|
293
|
+
|
294
|
+
# Query the Sphinx daemon - defaulting to all indices, but you can specify
|
296
295
|
# a specific one if you wish. The search parameter should be a string
|
297
296
|
# following Sphinx's expectations.
|
298
297
|
#
|
299
298
|
# The object returned from this method is a hash with the following keys:
|
300
|
-
#
|
299
|
+
#
|
301
300
|
# * :matches
|
302
301
|
# * :fields
|
303
302
|
# * :attributes
|
@@ -311,24 +310,24 @@ module Riddle
|
|
311
310
|
# * :error (if appropriate)
|
312
311
|
#
|
313
312
|
# The key <tt>:matches</tt> returns an array of hashes - the actual search
|
314
|
-
# results. Each hash has the document id (<tt>:doc</tt>), the result
|
313
|
+
# results. Each hash has the document id (<tt>:doc</tt>), the result
|
315
314
|
# weighting (<tt>:weight</tt>), and a hash of the attributes for the
|
316
315
|
# document (<tt>:attributes</tt>).
|
317
|
-
#
|
316
|
+
#
|
318
317
|
# The <tt>:fields</tt> and <tt>:attribute_names</tt> keys return list of
|
319
318
|
# fields and attributes for the documents. The key <tt>:attributes</tt>
|
320
319
|
# will return a hash of attribute name and type pairs, and <tt>:words</tt>
|
321
320
|
# returns a hash of hashes representing the words from the search, with the
|
322
321
|
# number of documents and hits for each, along the lines of:
|
323
|
-
#
|
322
|
+
#
|
324
323
|
# results[:words]["Pat"] #=> {:docs => 12, :hits => 15}
|
325
|
-
#
|
324
|
+
#
|
326
325
|
# <tt>:total</tt>, <tt>:total_found</tt> and <tt>:time</tt> return the
|
327
326
|
# number of matches available, the total number of matches (which may be
|
328
327
|
# greater than the maximum available, depending on the number of matches
|
329
328
|
# and your sphinx configuration), and the time in milliseconds that the
|
330
329
|
# query took to run.
|
331
|
-
#
|
330
|
+
#
|
332
331
|
# <tt>:status</tt> is the error code for the query - and if there was a
|
333
332
|
# related warning, it will be under the <tt>:warning</tt> key. Fatal errors
|
334
333
|
# will be described under <tt>:error</tt>.
|
@@ -337,7 +336,7 @@ module Riddle
|
|
337
336
|
@queue << query_message(search, index, comments)
|
338
337
|
self.run.first
|
339
338
|
end
|
340
|
-
|
339
|
+
|
341
340
|
# Build excerpts from search terms (the +words+) and the text of documents. Excerpts are bodies of text that have the +words+ highlighted.
|
342
341
|
# They may also be abbreviated to fit within a word limit.
|
343
342
|
#
|
@@ -368,13 +367,13 @@ module Riddle
|
|
368
367
|
#
|
369
368
|
# client.excerpts(:docs => ["Pat Allan, #{lorem_lipsum} Pat Cash"], :words => 'Pat', :index => 'pats')
|
370
369
|
# #=> ["<span class=\"match\">Pat</span> Allan, Lorem ipsum dolor sit amet, consectetur adipisicing
|
371
|
-
# elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua … . Excepteur
|
372
|
-
# sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est
|
373
|
-
# laborum. <span class=\"match\">Pat</span> Cash"]
|
370
|
+
# elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua … . Excepteur
|
371
|
+
# sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est
|
372
|
+
# laborum. <span class=\"match\">Pat</span> Cash"]
|
374
373
|
#
|
375
374
|
# Workflow:
|
376
375
|
#
|
377
|
-
# Excerpt creation is completely isolated from searching the index. The nominated index is only used to
|
376
|
+
# Excerpt creation is completely isolated from searching the index. The nominated index is only used to
|
378
377
|
# discover encoding and charset information.
|
379
378
|
#
|
380
379
|
# Therefore, the workflow goes:
|
@@ -402,30 +401,30 @@ module Riddle
|
|
402
401
|
options[:allow_empty] ||= false
|
403
402
|
options[:passage_boundary] ||= 'none'
|
404
403
|
options[:emit_zones] ||= false
|
405
|
-
|
404
|
+
|
406
405
|
response = Response.new request(:excerpt, excerpts_message(options))
|
407
|
-
|
406
|
+
|
408
407
|
options[:docs].collect { response.next }
|
409
408
|
end
|
410
|
-
|
409
|
+
|
411
410
|
# Update attributes - first parameter is the relevant index, second is an
|
412
411
|
# array of attributes to be updated, and the third is a hash, where the
|
413
412
|
# keys are the document ids, and the values are arrays with the attribute
|
414
413
|
# values - in the same order as the second parameter.
|
415
414
|
#
|
416
415
|
# Example:
|
417
|
-
#
|
416
|
+
#
|
418
417
|
# client.update('people', ['birthday'], {1 => [Time.at(1982, 20, 8).to_i]})
|
419
|
-
#
|
418
|
+
#
|
420
419
|
def update(index, attributes, values_by_doc)
|
421
420
|
response = Response.new request(
|
422
421
|
:update,
|
423
422
|
update_message(index, attributes, values_by_doc)
|
424
423
|
)
|
425
|
-
|
424
|
+
|
426
425
|
response.next_int
|
427
426
|
end
|
428
|
-
|
427
|
+
|
429
428
|
# Generates a keyword list for a given query. Each keyword is represented
|
430
429
|
# by a hash, with keys :tokenised and :normalised. If return_hits is set to
|
431
430
|
# true it will also report on the number of hits and documents for each
|
@@ -435,65 +434,65 @@ module Riddle
|
|
435
434
|
:keywords,
|
436
435
|
keywords_message(query, index, return_hits)
|
437
436
|
)
|
438
|
-
|
437
|
+
|
439
438
|
(0...response.next_int).collect do
|
440
439
|
hash = {}
|
441
440
|
hash[:tokenised] = response.next
|
442
441
|
hash[:normalised] = response.next
|
443
|
-
|
442
|
+
|
444
443
|
if return_hits
|
445
444
|
hash[:docs] = response.next_int
|
446
445
|
hash[:hits] = response.next_int
|
447
446
|
end
|
448
|
-
|
447
|
+
|
449
448
|
hash
|
450
449
|
end
|
451
450
|
end
|
452
|
-
|
451
|
+
|
453
452
|
def status
|
454
453
|
response = Response.new request(
|
455
454
|
:status, Message.new
|
456
455
|
)
|
457
|
-
|
456
|
+
|
458
457
|
rows, cols = response.next_int, response.next_int
|
459
|
-
|
458
|
+
|
460
459
|
(0...rows).inject({}) do |hash, row|
|
461
460
|
hash[response.next.to_sym] = response.next
|
462
461
|
hash
|
463
462
|
end
|
464
463
|
end
|
465
|
-
|
464
|
+
|
466
465
|
def flush_attributes
|
467
466
|
response = Response.new request(
|
468
467
|
:flushattrs, Message.new
|
469
468
|
)
|
470
|
-
|
469
|
+
|
471
470
|
response.next_int
|
472
471
|
end
|
473
|
-
|
472
|
+
|
474
473
|
def add_override(attribute, type, values)
|
475
474
|
@overrides[attribute] = {:type => type, :values => values}
|
476
475
|
end
|
477
|
-
|
476
|
+
|
478
477
|
def open
|
479
478
|
open_socket
|
480
|
-
|
479
|
+
|
481
480
|
return if Versions[:search] < 0x116
|
482
|
-
|
481
|
+
|
483
482
|
@socket.send [
|
484
483
|
Commands[:persist], 0, 4, 1
|
485
484
|
].pack("nnNN"), 0
|
486
485
|
end
|
487
|
-
|
486
|
+
|
488
487
|
def close
|
489
488
|
close_socket
|
490
489
|
end
|
491
|
-
|
490
|
+
|
492
491
|
private
|
493
|
-
|
492
|
+
|
494
493
|
def open_socket
|
495
494
|
raise "Already Connected" unless @socket.nil?
|
496
|
-
|
495
|
+
|
497
496
|
if @timeout == 0
|
498
497
|
@socket = initialise_connection
|
499
498
|
else
|
@@ -513,19 +512,19 @@ module Riddle
|
|
513
512
|
end
|
514
513
|
end
|
515
514
|
end
|
516
|
-
|
515
|
+
|
517
516
|
true
|
518
517
|
end
|
519
|
-
|
518
|
+
|
520
519
|
def close_socket
|
521
520
|
raise "Not Connected" if @socket.nil?
|
522
|
-
|
521
|
+
|
523
522
|
@socket.close
|
524
523
|
@socket = nil
|
525
|
-
|
524
|
+
|
526
525
|
true
|
527
526
|
end
|
528
|
-
|
527
|
+
|
529
528
|
# If there's an active connection to the Sphinx daemon, this will yield the
|
530
529
|
# socket. If there's no active connection, then it will connect, yield the
|
531
530
|
# new socket, then close it.
|
@@ -542,23 +541,23 @@ module Riddle
|
|
542
541
|
yield @socket
|
543
542
|
end
|
544
543
|
end
|
545
|
-
|
544
|
+
|
546
545
|
def initialise_connection
|
547
546
|
socket = initialise_socket
|
548
|
-
|
547
|
+
|
549
548
|
# Checking version
|
550
549
|
version = socket.recv(4).unpack('N*').first
|
551
550
|
if version < 1
|
552
551
|
socket.close
|
553
552
|
raise VersionError, "Can only connect to searchd version 1.0 or better, not version #{version}"
|
554
553
|
end
|
555
|
-
|
554
|
+
|
556
555
|
# Send version
|
557
556
|
socket.send [1].pack('N'), 0
|
558
|
-
|
557
|
+
|
559
558
|
socket
|
560
559
|
end
|
561
|
-
|
560
|
+
|
562
561
|
def initialise_socket
|
563
562
|
tries = 0
|
564
563
|
begin
|
@@ -571,15 +570,15 @@ module Riddle
|
|
571
570
|
else
|
572
571
|
TCPSocket.new server, @port
|
573
572
|
end
|
574
|
-
rescue Errno::ECONNREFUSED => e
|
573
|
+
rescue Errno::ETIMEDOUT, Errno::ECONNRESET, Errno::ECONNREFUSED => e
|
575
574
|
retry if (tries += 1) < 5
|
576
575
|
raise Riddle::ConnectionError,
|
577
576
|
"Connection to #{server} on #{@port} failed. #{e.message}"
|
578
577
|
end
|
579
|
-
|
578
|
+
|
580
579
|
socket
|
581
580
|
end
|
582
|
-
|
581
|
+
|
583
582
|
def request_header(command, length = 0)
|
584
583
|
length += key_message.length if key
|
585
584
|
core_header = case command
|
@@ -596,10 +595,10 @@ module Riddle
|
|
596
595
|
else
|
597
596
|
[Commands[command], Versions[command], length].pack("nnN")
|
598
597
|
end
|
599
|
-
|
598
|
+
|
600
599
|
key ? core_header + key_message : core_header
|
601
600
|
end
|
602
|
-
|
601
|
+
|
603
602
|
def key_message
|
604
603
|
@key_message ||= begin
|
605
604
|
message = Message.new
|
@@ -607,7 +606,7 @@ module Riddle
|
|
607
606
|
message.to_s
|
608
607
|
end
|
609
608
|
end
|
610
|
-
|
609
|
+
|
611
610
|
# Send a collection of messages, for a command type (eg, search, excerpts,
|
612
611
|
# update), to the Sphinx daemon.
|
613
612
|
def request(command, messages)
|
@@ -616,12 +615,12 @@ module Riddle
|
|
616
615
|
version = 0
|
617
616
|
length = 0
|
618
617
|
message = Riddle.encode(Array(messages).join(""), 'ASCII-8BIT')
|
619
|
-
|
618
|
+
|
620
619
|
connect do |socket|
|
621
620
|
case command
|
622
621
|
when :search
|
623
622
|
if Versions[command] >= 0x118
|
624
|
-
socket.send request_header(command, message.length) +
|
623
|
+
socket.send request_header(command, message.length) +
|
625
624
|
[0, messages.length].pack('NN') + message, 0
|
626
625
|
else
|
627
626
|
socket.send request_header(command, message.length) +
|
@@ -632,24 +631,24 @@ module Riddle
|
|
632
631
|
else
|
633
632
|
socket.send request_header(command, message.length) + message, 0
|
634
633
|
end
|
635
|
-
|
634
|
+
|
636
635
|
header = socket.recv(8)
|
637
636
|
status, version, length = header.unpack('n2N')
|
638
|
-
|
637
|
+
|
639
638
|
while response.length < (length || 0)
|
640
639
|
part = socket.recv(length - response.length)
|
641
640
|
|
642
641
|
# will return 0 bytes if remote side closed TCP connection, e.g, searchd segfaulted.
|
643
|
-
break if part.length == 0 && socket.is_a?(
|
642
|
+
break if part.length == 0 && socket.is_a?(TCPSocket)
|
644
643
|
|
645
644
|
response << part if part
|
646
645
|
end
|
647
646
|
end
|
648
|
-
|
647
|
+
|
649
648
|
if response.empty? || response.length != length
|
650
649
|
raise ResponseError, "No response from searchd (status: #{status}, version: #{version})"
|
651
650
|
end
|
652
|
-
|
651
|
+
|
653
652
|
case status
|
654
653
|
when Statuses[:ok]
|
655
654
|
if version < Versions[command]
|
@@ -670,34 +669,34 @@ module Riddle
|
|
670
669
|
raise ResponseError, "Unknown searchd error (status: #{status})"
|
671
670
|
end
|
672
671
|
end
|
673
|
-
|
672
|
+
|
674
673
|
# Generation of the message to send to Sphinx for a search.
|
675
674
|
def query_message(search, index, comments = '')
|
676
675
|
message = Message.new
|
677
|
-
|
676
|
+
|
678
677
|
# Mode, Limits, Sort Mode
|
679
678
|
message.append_ints @offset, @limit, MatchModes[@match_mode],
|
680
679
|
RankModes[@rank_mode], SortModes[@sort_mode]
|
681
680
|
message.append_string @sort_by
|
682
|
-
|
681
|
+
|
683
682
|
# Query
|
684
683
|
message.append_string search
|
685
|
-
|
684
|
+
|
686
685
|
# Weights
|
687
686
|
message.append_int @weights.length
|
688
687
|
message.append_ints *@weights
|
689
|
-
|
688
|
+
|
690
689
|
# Index
|
691
690
|
message.append_string index
|
692
|
-
|
691
|
+
|
693
692
|
# ID Range
|
694
693
|
message.append_int 1
|
695
694
|
message.append_64bit_ints @id_range.first, @id_range.last
|
696
|
-
|
695
|
+
|
697
696
|
# Filters
|
698
697
|
message.append_int @filters.length
|
699
698
|
@filters.each { |filter| message.append filter.query_message }
|
700
|
-
|
699
|
+
|
701
700
|
# Grouping
|
702
701
|
message.append_int GroupFunctions[@group_function]
|
703
702
|
message.append_string @group_by
|
@@ -705,7 +704,7 @@ module Riddle
|
|
705
704
|
message.append_string @group_clause
|
706
705
|
message.append_ints @cut_off, @retry_count, @retry_delay
|
707
706
|
message.append_string @group_distinct
|
708
|
-
|
707
|
+
|
709
708
|
# Anchor Point
|
710
709
|
if @anchor.empty?
|
711
710
|
message.append_int 0
|
@@ -715,29 +714,29 @@ module Riddle
|
|
715
714
|
message.append_string @anchor[:longitude_attribute]
|
716
715
|
message.append_floats @anchor[:latitude], @anchor[:longitude]
|
717
716
|
end
|
718
|
-
|
717
|
+
|
719
718
|
# Per Index Weights
|
720
719
|
message.append_int @index_weights.length
|
721
720
|
@index_weights.each do |key,val|
|
722
721
|
message.append_string key.to_s
|
723
722
|
message.append_int val
|
724
723
|
end
|
725
|
-
|
724
|
+
|
726
725
|
# Max Query Time
|
727
726
|
message.append_int @max_query_time
|
728
|
-
|
727
|
+
|
729
728
|
# Per Field Weights
|
730
729
|
message.append_int @field_weights.length
|
731
730
|
@field_weights.each do |key,val|
|
732
731
|
message.append_string key.to_s
|
733
732
|
message.append_int val
|
734
733
|
end
|
735
|
-
|
734
|
+
|
736
735
|
message.append_string comments
|
737
|
-
|
736
|
+
|
738
737
|
return message.to_s if Versions[:search] < 0x116
|
739
|
-
|
740
|
-
# Overrides
|
738
|
+
|
739
|
+
# Overrides
|
741
740
|
message.append_int @overrides.length
|
742
741
|
@overrides.each do |key,val|
|
743
742
|
message.append_string key.to_s
|
@@ -756,56 +755,56 @@ module Riddle
|
|
756
755
|
message.send method, map
|
757
756
|
end
|
758
757
|
end
|
759
|
-
|
758
|
+
|
760
759
|
message.append_string @select
|
761
|
-
|
760
|
+
|
762
761
|
message.to_s
|
763
762
|
end
|
764
|
-
|
763
|
+
|
765
764
|
# Generation of the message to send to Sphinx for an excerpts request.
|
766
765
|
def excerpts_message(options)
|
767
766
|
message = Message.new
|
768
|
-
|
767
|
+
|
769
768
|
message.append [0, excerpt_flags(options)].pack('N2') # 0 = mode
|
770
769
|
message.append_string options[:index]
|
771
770
|
message.append_string options[:words]
|
772
|
-
|
771
|
+
|
773
772
|
# options
|
774
773
|
message.append_string options[:before_match]
|
775
774
|
message.append_string options[:after_match]
|
776
775
|
message.append_string options[:chunk_separator]
|
777
776
|
message.append_ints options[:limit], options[:around]
|
778
|
-
|
777
|
+
|
779
778
|
message.append_array options[:docs]
|
780
|
-
|
779
|
+
|
781
780
|
message.to_s
|
782
781
|
end
|
783
|
-
|
782
|
+
|
784
783
|
# Generation of the message to send to Sphinx to update attributes of a
|
785
784
|
# document.
|
786
785
|
def update_message(index, attributes, values_by_doc)
|
787
786
|
message = Message.new
|
788
|
-
|
787
|
+
|
789
788
|
message.append_string index
|
790
789
|
message.append_array attributes
|
791
|
-
|
790
|
+
|
792
791
|
message.append_int values_by_doc.length
|
793
792
|
values_by_doc.each do |key,values|
|
794
793
|
message.append_64bit_int key # document ID
|
795
794
|
message.append_ints *values # array of new values (integers)
|
796
795
|
end
|
797
|
-
|
796
|
+
|
798
797
|
message.to_s
|
799
798
|
end
|
800
|
-
|
799
|
+
|
801
800
|
# Generates the simple message to send to the daemon for a keywords request.
|
802
801
|
def keywords_message(query, index, return_hits)
|
803
802
|
message = Message.new
|
804
|
-
|
803
|
+
|
805
804
|
message.append_string query
|
806
805
|
message.append_string index
|
807
806
|
message.append_int return_hits ? 1 : 0
|
808
|
-
|
807
|
+
|
809
808
|
message.to_s
|
810
809
|
end
|
811
810
|
|
@@ -826,12 +825,12 @@ module Riddle
|
|
826
825
|
AttributeTypes[:multi] + AttributeTypes[:bigint] => :next_64bit_int_array,
|
827
826
|
AttributeTypes[:multi] + AttributeTypes[:string] => :next_array
|
828
827
|
}
|
829
|
-
|
828
|
+
|
830
829
|
def attribute_from_type(type, response)
|
831
830
|
handler = AttributeHandlers[type]
|
832
831
|
response.send handler
|
833
832
|
end
|
834
|
-
|
833
|
+
|
835
834
|
def excerpt_flags(options)
|
836
835
|
flags = 1
|
837
836
|
flags |= 2 if options[:exact_phrase]
|