riddle 0.9.8.1533.10 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. data/README.textile +1 -0
  2. data/lib/riddle.rb +9 -15
  3. data/lib/riddle/0.9.8.rb +0 -0
  4. data/lib/riddle/0.9.9.rb +5 -0
  5. data/lib/riddle/0.9.9/client.rb +49 -0
  6. data/lib/riddle/0.9.9/client/filter.rb +22 -0
  7. data/lib/riddle/0.9.9/configuration/searchd.rb +28 -0
  8. data/lib/riddle/client.rb +110 -18
  9. data/lib/riddle/client/filter.rb +29 -20
  10. data/lib/riddle/client/message.rb +4 -0
  11. data/lib/riddle/client/response.rb +10 -0
  12. data/lib/riddle/configuration/distributed_index.rb +8 -7
  13. data/lib/riddle/configuration/index.rb +15 -11
  14. data/lib/riddle/configuration/searchd.rb +6 -4
  15. data/lib/riddle/configuration/sql_source.rb +9 -4
  16. data/lib/riddle/controller.rb +5 -3
  17. data/spec/fixtures/data_generator.0.9.8.php +208 -0
  18. data/spec/fixtures/data_generator.0.9.9.php +225 -0
  19. data/spec/fixtures/sphinx/configuration.erb +38 -0
  20. data/spec/fixtures/sphinx/people.spa +0 -0
  21. data/spec/fixtures/sphinx/people.spd +0 -0
  22. data/spec/fixtures/sphinx/people.sph +0 -0
  23. data/spec/fixtures/sphinx/people.spi +0 -0
  24. data/spec/fixtures/sphinx/people.spk +0 -0
  25. data/spec/fixtures/sphinx/people.spm +0 -0
  26. data/spec/fixtures/sphinx/people.spp +0 -0
  27. data/spec/fixtures/sphinx/searchd.log +3731 -0
  28. data/spec/fixtures/sphinx/searchd.query.log +1032 -0
  29. data/spec/fixtures/sphinx/spec.conf +38 -0
  30. data/spec/fixtures/sphinxapi.0.9.8.php +1228 -0
  31. data/spec/fixtures/sphinxapi.0.9.9.php +1646 -0
  32. data/spec/fixtures/sql/conf.example.yml +3 -0
  33. data/spec/fixtures/sql/conf.yml +3 -0
  34. data/spec/fixtures/sql/data.sql +25000 -0
  35. data/spec/fixtures/sql/structure.sql +16 -0
  36. data/spec/functional/excerpt_spec.rb +37 -10
  37. data/spec/functional/persistance_spec.rb +17 -0
  38. data/spec/functional/status_spec.rb +21 -0
  39. data/spec/functional/update_spec.rb +3 -3
  40. data/spec/spec_helper.rb +30 -0
  41. data/spec/sphinx_helper.rb +93 -0
  42. data/spec/unit/client_spec.rb +20 -3
  43. data/spec/unit/configuration/distributed_index_spec.rb +2 -0
  44. data/spec/unit/configuration/index_spec.rb +16 -0
  45. data/spec/unit/configuration/searchd_spec.rb +46 -13
  46. data/spec/unit/configuration/sql_source_spec.rb +15 -0
  47. metadata +61 -37
  48. data/MIT-LICENCE +0 -20
  49. data/lib/tabtab_definitions.rb +0 -15
@@ -87,3 +87,4 @@ Thanks to the following people who have contributed to Riddle in some shape or f
87
87
  * Kristopher Chambers
88
88
  * Rob Anderton
89
89
  * Dylan Egan
90
+ * Jerry Vos
@@ -7,24 +7,18 @@ require 'riddle/controller'
7
7
 
8
8
  module Riddle #:nodoc:
9
9
  class ConnectionError < StandardError #:nodoc:
10
+ #
10
11
  end
11
12
 
12
- module Version #:nodoc:
13
- Major = 0
14
- Minor = 9
15
- Tiny = 8
16
- # Revision number for RubyForge's sake, taken from what Sphinx
17
- # outputs to the command line.
18
- Rev = 1533
19
- # Release number to mark my own fixes, beyond feature parity with
20
- # Sphinx itself.
21
- Release = 10
22
-
23
- String = [Major, Minor, Tiny].join('.')
24
- GemVersion = [Major, Minor, Tiny, Rev, Release].join('.')
13
+ def self.escape_pattern
14
+ Thread.current[:riddle_escape_pattern] ||= /[\(\)\|\-!@~"&\/]/
15
+ end
16
+
17
+ def self.escape_pattern=(pattern)
18
+ Thread.current[:riddle_escape_pattern] = pattern
25
19
  end
26
20
 
27
21
  def self.escape(string)
28
- string.gsub(/[\(\)\|\-!@~"&\/]/) { |char| "\\#{char}" }
22
+ string.gsub(escape_pattern) { |char| "\\#{char}" }
29
23
  end
30
- end
24
+ end
File without changes
@@ -0,0 +1,5 @@
1
+ require 'riddle/0.9.9/client'
2
+ require 'riddle/0.9.9/client/filter'
3
+ require 'riddle/0.9.9/configuration/searchd'
4
+
5
+ Riddle.escape_pattern = /[\(\)\|\-!@~"&\/\\\^\$=]/
@@ -0,0 +1,49 @@
1
+ Riddle::Client::Versions[:search] = 0x116
2
+ Riddle::Client::Versions[:update] = 0x102
3
+
4
+ class Riddle::Client
5
+ private
6
+
7
+ def initialise_connection
8
+ socket = initialise_socket
9
+
10
+ # Send version
11
+ socket.send [1].pack('N'), 0
12
+
13
+ # Checking version
14
+ version = socket.recv(4).unpack('N*').first
15
+ if version < 1
16
+ socket.close
17
+ raise VersionError, "Can only connect to searchd version 1.0 or better, not version #{version}"
18
+ end
19
+
20
+ socket
21
+ end
22
+
23
+ def update_message(index, attributes, values_by_doc)
24
+ message = Message.new
25
+
26
+ message.append_string index
27
+ message.append_int attributes.length
28
+ attributes.each_with_index do |attribute, index|
29
+ message.append_string attribute
30
+ message.append_boolean values_by_doc.values.first[index].is_a?(Array)
31
+ end
32
+
33
+ message.append_int values_by_doc.length
34
+ values_by_doc.each do |key,values|
35
+ message.append_64bit_int key # document ID
36
+ values.each do |value|
37
+ case value
38
+ when Array
39
+ message.append_int value.length
40
+ message.append_ints *value
41
+ else
42
+ message.append_int value
43
+ end
44
+ end
45
+ end
46
+
47
+ message.to_s
48
+ end
49
+ end
@@ -0,0 +1,22 @@
1
+ class Riddle::Client::Filter
2
+ #
3
+
4
+ private
5
+
6
+ def append_integer_range(message, range)
7
+ message.append_64bit_ints self.values.first, self.values.last
8
+ end
9
+
10
+ def append_array(message, array)
11
+ message.append_64bit_ints *array.collect { |val|
12
+ case val
13
+ when TrueClass
14
+ 1
15
+ when FalseClass
16
+ 0
17
+ else
18
+ val
19
+ end
20
+ }
21
+ end
22
+ end
@@ -0,0 +1,28 @@
1
+ module Riddle
2
+ class Configuration
3
+ class Searchd
4
+ def valid?
5
+ set_listen
6
+ clear_deprecated
7
+
8
+ !( @listen.nil? || @pid_file.nil? )
9
+ end
10
+
11
+ private
12
+
13
+ def set_listen
14
+ return unless @listen.nil?
15
+
16
+ @listen = @port.to_s if @port && @address.nil?
17
+ @listen = "#{@address}:#{@port}" if @address && @port
18
+ end
19
+
20
+ def clear_deprecated
21
+ return if @listen.nil?
22
+
23
+ @address = nil
24
+ @port = nil
25
+ end
26
+ end
27
+ end
28
+ end
@@ -33,14 +33,19 @@ module Riddle
33
33
  :search => 0, # SEARCHD_COMMAND_SEARCH
34
34
  :excerpt => 1, # SEARCHD_COMMAND_EXCERPT
35
35
  :update => 2, # SEARCHD_COMMAND_UPDATE
36
- :keywords => 3 # SEARCHD_COMMAND_KEYWORDS
36
+ :keywords => 3, # SEARCHD_COMMAND_KEYWORDS
37
+ :persist => 4, # SEARCHD_COMMAND_PERSIST
38
+ :status => 5, # SEARCHD_COMMAND_STATUS
39
+ :query => 6 # SEARCHD_COMMAND_QUERY
37
40
  }
38
41
 
39
42
  Versions = {
40
43
  :search => 0x113, # VER_COMMAND_SEARCH
41
44
  :excerpt => 0x100, # VER_COMMAND_EXCERPT
42
45
  :update => 0x101, # VER_COMMAND_UPDATE
43
- :keywords => 0x100 # VER_COMMAND_KEYWORDS
46
+ :keywords => 0x100, # VER_COMMAND_KEYWORDS
47
+ :status => 0x100, # VER_COMMAND_STATUS
48
+ :query => 0x100 # VER_COMMAND_QUERY
44
49
  }
45
50
 
46
51
  Statuses = {
@@ -64,7 +69,10 @@ module Riddle
64
69
  :proximity_bm25 => 0, # SPH_RANK_PROXIMITY_BM25
65
70
  :bm25 => 1, # SPH_RANK_BM25
66
71
  :none => 2, # SPH_RANK_NONE
67
- :wordcount => 3 # SPH_RANK_WORDCOUNT
72
+ :wordcount => 3, # SPH_RANK_WORDCOUNT
73
+ :proximity => 4, # SPH_RANK_PROXIMITY
74
+ :match_any => 5, # SPH_RANK_MATCHANY
75
+ :fieldmask => 6 # SPH_RANK_FIELDMASK
68
76
  }
69
77
 
70
78
  SortModes = {
@@ -82,6 +90,7 @@ module Riddle
82
90
  :ordinal => 3, # SPH_ATTR_ORDINAL
83
91
  :bool => 4, # SPH_ATTR_BOOL
84
92
  :float => 5, # SPH_ATTR_FLOAT
93
+ :bigint => 6, # SPH_ATTR_BIGINT
85
94
  :multi => 0x40000000 # SPH_ATTR_MULTI
86
95
  }
87
96
 
@@ -104,7 +113,7 @@ module Riddle
104
113
  :match_mode, :sort_mode, :sort_by, :weights, :id_range, :filters,
105
114
  :group_by, :group_function, :group_clause, :group_distinct, :cut_off,
106
115
  :retry_count, :retry_delay, :anchor, :index_weights, :rank_mode,
107
- :max_query_time, :field_weights, :timeout
116
+ :max_query_time, :field_weights, :timeout, :overrides, :select
108
117
  attr_reader :queue
109
118
 
110
119
  # Can instantiate with a specific server and port - otherwise it assumes
@@ -113,6 +122,7 @@ module Riddle
113
122
  def initialize(server=nil, port=nil)
114
123
  @server = server || "localhost"
115
124
  @port = port || 3312
125
+ @socket = nil
116
126
 
117
127
  reset
118
128
 
@@ -146,6 +156,8 @@ module Riddle
146
156
  # string keys are field names, integer values are weightings
147
157
  @field_weights = {}
148
158
  @timeout = 0
159
+ @overrides = {}
160
+ @select = "*"
149
161
  end
150
162
 
151
163
  # Set the geo-anchor point - with the names of the attributes that contain
@@ -389,27 +401,77 @@ module Riddle
389
401
  end
390
402
  end
391
403
 
404
+ def status
405
+ response = Response.new request(
406
+ :status, Message.new
407
+ )
408
+
409
+ rows, cols = response.next_int, response.next_int
410
+
411
+ (0...rows).inject({}) do |hash, row|
412
+ hash[response.next.to_sym] = response.next
413
+ hash
414
+ end
415
+ end
416
+
417
+ def add_override(attribute, type, values)
418
+ @overrides[attribute] = {:type => type, :values => values}
419
+ end
420
+
421
+ def open
422
+ open_socket
423
+
424
+ return if Versions[:search] < 0x116
425
+
426
+ @socket.send [
427
+ Commands[:persist], 0, 4, 1
428
+ ].pack("nnNN"), 0
429
+ end
430
+
431
+ def close
432
+ close_socket
433
+ end
434
+
392
435
  private
393
436
 
394
- # Connects to the Sphinx daemon, and yields a socket to use. The socket is
395
- # closed at the end of the block.
396
- def connect(&block)
397
- socket = nil
437
+ def open_socket
438
+ raise "Already Connected" unless @socket.nil?
439
+
398
440
  if @timeout == 0
399
- socket = initialise_connection
441
+ @socket = initialise_connection
400
442
  else
401
443
  begin
402
- Timeout.timeout(@timeout) { socket = initialise_connection }
444
+ Timeout.timeout(@timeout) { @socket = initialise_connection }
403
445
  rescue Timeout::Error
404
446
  raise Riddle::ConnectionError,
405
447
  "Connection to #{@server} on #{@port} timed out after #{@timeout} seconds"
406
448
  end
407
449
  end
408
-
409
- begin
410
- yield socket
411
- ensure
412
- socket.close
450
+
451
+ true
452
+ end
453
+
454
+ def close_socket
455
+ raise "Not Connected" if @socket.nil?
456
+
457
+ @socket.close
458
+ @socket = nil
459
+
460
+ true
461
+ end
462
+
463
+ # Connects to the Sphinx daemon, and yields a socket to use. The socket is
464
+ # closed at the end of the block.
465
+ def connect(&block)
466
+ unless @socket.nil?
467
+ yield @socket
468
+ else
469
+ open_socket
470
+ begin
471
+ yield @socket
472
+ ensure
473
+ close_socket
474
+ end
413
475
  end
414
476
  end
415
477
 
@@ -453,7 +515,7 @@ module Riddle
453
515
  if message.respond_to?(:force_encoding)
454
516
  message = message.force_encoding('ASCII-8BIT')
455
517
  end
456
-
518
+
457
519
  connect do |socket|
458
520
  case command
459
521
  when :search
@@ -463,6 +525,10 @@ module Riddle
463
525
  Commands[command], Versions[command],
464
526
  4+message.length, messages.length
465
527
  ].pack("nnNN") + message, 0
528
+ when :status
529
+ socket.send [
530
+ Commands[command], Versions[command], 4, 1
531
+ ].pack("nnNN"), 0
466
532
  else
467
533
  socket.send [
468
534
  Commands[command], Versions[command], message.length
@@ -565,6 +631,30 @@ module Riddle
565
631
 
566
632
  message.append_string comments
567
633
 
634
+ return message.to_s if Versions[:search] < 0x116
635
+
636
+ # Overrides
637
+ message.append_int @overrides.length
638
+ @overrides.each do |key,val|
639
+ message.append_string key.to_s
640
+ message.append_int AttributeTypes[val[:type]]
641
+ message.append_int val[:values].length
642
+ val[:values].each do |id,map|
643
+ message.append_64bit_int id
644
+ method = case val[:type]
645
+ when :float
646
+ :append_float
647
+ when :bigint
648
+ :append_64bit_int
649
+ else
650
+ :append_int
651
+ end
652
+ message.send method, map
653
+ end
654
+ end
655
+
656
+ message.append_string @select
657
+
568
658
  message.to_s
569
659
  end
570
660
 
@@ -626,9 +716,11 @@ module Riddle
626
716
 
627
717
  case type
628
718
  when AttributeTypes[:float]
629
- is_multi ? response.next_float_array : response.next_float
719
+ is_multi ? response.next_float_array : response.next_float
720
+ when AttributeTypes[:bigint]
721
+ is_multi ? response.next_64bit_int_arry : response.next_64bit_int
630
722
  else
631
- is_multi ? response.next_int_array : response.next_int
723
+ is_multi ? response.next_int_array : response.next_int
632
724
  end
633
725
  end
634
726
  end
@@ -1,23 +1,22 @@
1
1
  module Riddle
2
2
  class Client
3
- # Used for querying Sphinx.
4
3
  class Filter
5
4
  attr_accessor :attribute, :values, :exclude
6
-
5
+
7
6
  # Attribute name, values (which can be an array or a range), and whether
8
7
  # the filter should be exclusive.
9
8
  def initialize(attribute, values, exclude=false)
10
9
  @attribute, @values, @exclude = attribute, values, exclude
11
10
  end
12
-
11
+
13
12
  def exclude?
14
13
  self.exclude
15
14
  end
16
-
15
+
17
16
  # Returns the message for this filter to send to the Sphinx service
18
17
  def query_message
19
18
  message = Message.new
20
-
19
+
21
20
  message.append_string self.attribute.to_s
22
21
  case self.values
23
22
  when Range
@@ -26,28 +25,38 @@ module Riddle
26
25
  message.append_floats self.values.first, self.values.last
27
26
  else
28
27
  message.append_int FilterTypes[:range]
29
- message.append_ints self.values.first, self.values.last
28
+ append_integer_range message, self.values
30
29
  end
31
30
  when Array
32
31
  message.append_int FilterTypes[:values]
33
32
  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|
37
- case val
38
- when TrueClass
39
- 1.0
40
- when FalseClass
41
- 0.0
42
- else
43
- val.to_f
44
- end
45
- }
33
+ append_array message, self.values
46
34
  end
47
35
  message.append_int self.exclude? ? 1 : 0
48
-
36
+
49
37
  message.to_s
50
38
  end
39
+
40
+ private
41
+
42
+ def append_integer_range(message, range)
43
+ message.append_ints self.values.first, self.values.last
44
+ end
45
+
46
+ # Using to_f is a hack from the PHP client - to workaround 32bit signed
47
+ # ints on x32 platforms
48
+ def append_array(message, array)
49
+ message.append_ints *array.collect { |val|
50
+ case val
51
+ when TrueClass
52
+ 1.0
53
+ when FalseClass
54
+ 0.0
55
+ else
56
+ val.to_f
57
+ end
58
+ }
59
+ end
51
60
  end
52
61
  end
53
- end
62
+ end