riddle 0.9.8.1533.10 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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