freelancing-god-riddle 0.9.8.1371.1
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENCE +20 -0
- data/README +85 -0
- data/lib/riddle/client/filter.rb +44 -0
- data/lib/riddle/client/message.rb +65 -0
- data/lib/riddle/client/response.rb +84 -0
- data/lib/riddle/client.rb +639 -0
- data/lib/riddle.rb +30 -0
- data/spec/functional/excerpt_spec.rb +102 -0
- data/spec/functional/keywords_spec.rb +40 -0
- data/spec/functional/search_spec.rb +69 -0
- data/spec/functional/update_spec.rb +41 -0
- data/spec/unit/client_spec.rb +194 -0
- data/spec/unit/filter_spec.rb +33 -0
- data/spec/unit/message_spec.rb +63 -0
- data/spec/unit/response_spec.rb +64 -0
- metadata +70 -0
data/MIT-LICENCE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2007 Pat Allan
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
This client has been written to interface with Sphinx[http://sphinxsearch.com/]. It is written by
|
2
|
+
{Pat Allan}[http://freelancing-gods.com], and has been influenced by both Dmytro Shteflyuk's Ruby
|
3
|
+
client and the original PHP client - credit where credit's due, after all.
|
4
|
+
|
5
|
+
It does not follow the same syntax as those two, though (not much point writing this otherwise) -
|
6
|
+
opting for a more Ruby-like structure.
|
7
|
+
|
8
|
+
The easiest way to install is to grab the gem from GitHub:
|
9
|
+
|
10
|
+
sudo gem install freelancing-god-riddle --source http://gems.github.com/
|
11
|
+
|
12
|
+
However, if you're so inclined, you can grab sourcecode.
|
13
|
+
|
14
|
+
git clone git://github.com/freelancing-god/thinking-sphinx.git
|
15
|
+
|
16
|
+
If you're looking for old versions - for older versions of Sphinx - you'll want to peruse the tagged versions in the old subversion repository
|
17
|
+
|
18
|
+
svn co http://rails-oceania.googlecode.com/svn/patallan/riddle/tags riddle-tags
|
19
|
+
|
20
|
+
Please note that at the time of writing, the following versions are supported (if you get the appropriate tag):
|
21
|
+
|
22
|
+
* 0.9.8-r871
|
23
|
+
* 0.9.8-r909
|
24
|
+
* 0.9.8-r985
|
25
|
+
* 0.9.8-r1065
|
26
|
+
* 0.9.8-r1112
|
27
|
+
* 0.9.8-rc1 (gem version: 0.9.8.1198)
|
28
|
+
* 0.9.8-rc2 (gem version: 0.9.8.1231)
|
29
|
+
* 0.9.8 (gem version: 0.9.8.1371)
|
30
|
+
|
31
|
+
To get started, just instantiate a Client object:
|
32
|
+
|
33
|
+
client = Riddle::Client.new # defaults to localhost and port 3312
|
34
|
+
client = Riddle::Client.new "sphinxserver.domain.tld", 3333 # custom settings
|
35
|
+
|
36
|
+
And then set the parameters to what you want, before running a query:
|
37
|
+
|
38
|
+
client.match_mode = :extended
|
39
|
+
client.query "Pat Allan @state Victoria"
|
40
|
+
|
41
|
+
The results from a query are similar to the other clients - but here's the details. It's a hash with
|
42
|
+
the following keys:
|
43
|
+
|
44
|
+
* :matches
|
45
|
+
* :fields
|
46
|
+
* :attributes
|
47
|
+
* :attribute_names
|
48
|
+
* :words
|
49
|
+
* :total
|
50
|
+
* :total_found
|
51
|
+
* :time
|
52
|
+
* :status
|
53
|
+
* :warning (if appropriate)
|
54
|
+
* :error (if appropriate)
|
55
|
+
|
56
|
+
The key <tt>:matches</tt> returns an array of hashes - the actual search results. Each hash has the
|
57
|
+
document id (<tt>:doc</tt>), the result weighting (<tt>:weight</tt>), and a hash of the attributes for
|
58
|
+
the document (<tt>:attributes</tt>).
|
59
|
+
|
60
|
+
The <tt>:fields</tt> and <tt>:attribute_names</tt> keys return list of fields and attributes for the
|
61
|
+
documents. The key <tt>:attributes</tt> will return a hash of attribute name and type pairs, and
|
62
|
+
<tt>:words</tt> returns a hash of hashes representing the words from the search, with the number of
|
63
|
+
documents and hits for each, along the lines of:
|
64
|
+
|
65
|
+
results[:words]["Pat"] #=> {:docs => 12, :hits => 15}
|
66
|
+
|
67
|
+
<tt>:total</tt>, <tt>:total_found</tt> and <tt>:time</tt> return the number of matches available, the
|
68
|
+
total number of matches (which may be greater than the maximum available), and the time in milliseconds
|
69
|
+
that the query took to run.
|
70
|
+
|
71
|
+
<tt>:status</tt> is the error code for the query - and if there was a related warning, it will be under
|
72
|
+
the <tt>:warning</tt> key. Fatal errors will be described under <tt>:error</tt>.
|
73
|
+
|
74
|
+
If you've installed the gem and wondering why there's no tests - check out the svn version. I've kept the specs out of the gem as I have a decent amount of test data in there, which really isn't needed unless you want to submit patches.
|
75
|
+
|
76
|
+
== Contributors
|
77
|
+
|
78
|
+
Thanks to the following people who have contributed to Riddle in some shape or form:
|
79
|
+
|
80
|
+
* Andrew Aksyonoff
|
81
|
+
* Brad Greenlee
|
82
|
+
* Lachie Cox
|
83
|
+
* Jeremy Seitz
|
84
|
+
* Mark Lane
|
85
|
+
* Xavier Noria
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Riddle
|
2
|
+
class Client
|
3
|
+
# Used for querying Sphinx.
|
4
|
+
class Filter
|
5
|
+
attr_accessor :attribute, :values, :exclude
|
6
|
+
|
7
|
+
# Attribute name, values (which can be an array or a range), and whether
|
8
|
+
# the filter should be exclusive.
|
9
|
+
def initialize(attribute, values, exclude=false)
|
10
|
+
@attribute, @values, @exclude = attribute, values, exclude
|
11
|
+
end
|
12
|
+
|
13
|
+
def exclude?
|
14
|
+
self.exclude
|
15
|
+
end
|
16
|
+
|
17
|
+
# Returns the message for this filter to send to the Sphinx service
|
18
|
+
def query_message
|
19
|
+
message = Message.new
|
20
|
+
|
21
|
+
message.append_string self.attribute.to_s
|
22
|
+
case self.values
|
23
|
+
when Range
|
24
|
+
if self.values.first.is_a?(Float) && self.values.last.is_a?(Float)
|
25
|
+
message.append_int FilterTypes[:float_range]
|
26
|
+
message.append_floats self.values.first, self.values.last
|
27
|
+
else
|
28
|
+
message.append_int FilterTypes[:range]
|
29
|
+
message.append_ints self.values.first, self.values.last
|
30
|
+
end
|
31
|
+
when Array
|
32
|
+
message.append_int FilterTypes[:values]
|
33
|
+
message.append_int self.values.length
|
34
|
+
# using to_f is a hack from the php client - to workaround 32bit
|
35
|
+
# signed ints on x32 platforms
|
36
|
+
message.append_ints *self.values.collect { |val| val.to_f }
|
37
|
+
end
|
38
|
+
message.append_int self.exclude? ? 1 : 0
|
39
|
+
|
40
|
+
message.to_s
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Riddle
|
2
|
+
class Client
|
3
|
+
# This class takes care of the translation of ints, strings and arrays to
|
4
|
+
# the format required by the Sphinx service.
|
5
|
+
class Message
|
6
|
+
def initialize
|
7
|
+
@message = ""
|
8
|
+
@size_method = @message.respond_to?(:bytesize) ? :bytesize : :length
|
9
|
+
end
|
10
|
+
|
11
|
+
# Append raw data (only use if you know what you're doing)
|
12
|
+
def append(*args)
|
13
|
+
return if args.length == 0
|
14
|
+
|
15
|
+
args.each { |arg| @message << arg }
|
16
|
+
end
|
17
|
+
|
18
|
+
# Append a string's length, then the string itself
|
19
|
+
def append_string(str)
|
20
|
+
@message << [str.send(@size_method)].pack('N') + str
|
21
|
+
end
|
22
|
+
|
23
|
+
# Append an integer
|
24
|
+
def append_int(int)
|
25
|
+
@message << [int].pack('N')
|
26
|
+
end
|
27
|
+
|
28
|
+
def append_64bit_int(int)
|
29
|
+
@message << [int >> 32, int & 0xFFFFFFFF].pack('NN')
|
30
|
+
end
|
31
|
+
|
32
|
+
# Append a float
|
33
|
+
def append_float(float)
|
34
|
+
@message << [float].pack('f').unpack('L*').pack("N")
|
35
|
+
end
|
36
|
+
|
37
|
+
# Append multiple integers
|
38
|
+
def append_ints(*ints)
|
39
|
+
ints.each { |int| append_int(int) }
|
40
|
+
end
|
41
|
+
|
42
|
+
def append_64bit_ints(*ints)
|
43
|
+
ints.each { |int| append_64bit_int(int) }
|
44
|
+
end
|
45
|
+
|
46
|
+
# Append multiple floats
|
47
|
+
def append_floats(*floats)
|
48
|
+
floats.each { |float| append_float(float) }
|
49
|
+
end
|
50
|
+
|
51
|
+
# Append an array of strings - first appends the length of the array,
|
52
|
+
# then each item's length and value.
|
53
|
+
def append_array(array)
|
54
|
+
append_int(array.length)
|
55
|
+
|
56
|
+
array.each { |item| append_string(item) }
|
57
|
+
end
|
58
|
+
|
59
|
+
# Returns the entire message
|
60
|
+
def to_s
|
61
|
+
@message
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module Riddle
|
2
|
+
class Client
|
3
|
+
# Used to interrogate responses from the Sphinx daemon. Keep in mind none
|
4
|
+
# of the methods here check whether the data they're grabbing are what the
|
5
|
+
# user expects - it just assumes the user knows what the data stream is
|
6
|
+
# made up of.
|
7
|
+
class Response
|
8
|
+
# Create with the data to interpret
|
9
|
+
def initialize(str)
|
10
|
+
@str = str
|
11
|
+
@marker = 0
|
12
|
+
end
|
13
|
+
|
14
|
+
# Return the next string value in the stream
|
15
|
+
def next
|
16
|
+
len = next_int
|
17
|
+
result = @str[@marker, len]
|
18
|
+
@marker += len
|
19
|
+
|
20
|
+
return result
|
21
|
+
end
|
22
|
+
|
23
|
+
# Return the next integer value from the stream
|
24
|
+
def next_int
|
25
|
+
int = @str[@marker, 4].unpack('N*').first
|
26
|
+
@marker += 4
|
27
|
+
|
28
|
+
return int
|
29
|
+
end
|
30
|
+
|
31
|
+
def next_64bit_int
|
32
|
+
high, low = @str[@marker, 8].unpack('N*N*')[0..1]
|
33
|
+
@marker += 8
|
34
|
+
|
35
|
+
return (high << 32) + low
|
36
|
+
end
|
37
|
+
|
38
|
+
# Return the next float value from the stream
|
39
|
+
def next_float
|
40
|
+
float = @str[@marker, 4].unpack('N*').pack('L').unpack('f*').first
|
41
|
+
@marker += 4
|
42
|
+
|
43
|
+
return float
|
44
|
+
end
|
45
|
+
|
46
|
+
# Returns an array of string items
|
47
|
+
def next_array
|
48
|
+
count = next_int
|
49
|
+
items = []
|
50
|
+
for i in 0...count
|
51
|
+
items << self.next
|
52
|
+
end
|
53
|
+
|
54
|
+
return items
|
55
|
+
end
|
56
|
+
|
57
|
+
# Returns an array of int items
|
58
|
+
def next_int_array
|
59
|
+
count = next_int
|
60
|
+
items = []
|
61
|
+
for i in 0...count
|
62
|
+
items << self.next_int
|
63
|
+
end
|
64
|
+
|
65
|
+
return items
|
66
|
+
end
|
67
|
+
|
68
|
+
def next_float_array
|
69
|
+
count = next_int
|
70
|
+
items = []
|
71
|
+
for i in 0...count
|
72
|
+
items << self.next_float
|
73
|
+
end
|
74
|
+
|
75
|
+
return items
|
76
|
+
end
|
77
|
+
|
78
|
+
# Returns the length of the streamed data
|
79
|
+
def length
|
80
|
+
@str.length
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|