shanna-dm-sphinx-adapter 0.5 → 0.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. data/Manifest.txt +12 -19
  2. data/README.txt +30 -38
  3. data/Rakefile +2 -3
  4. data/dm-sphinx-adapter.gemspec +6 -9
  5. data/lib/dm-sphinx-adapter/adapter.rb +87 -73
  6. data/lib/dm-sphinx-adapter/attribute.rb +56 -12
  7. data/lib/dm-sphinx-adapter/index.rb +14 -1
  8. data/lib/dm-sphinx-adapter/query.rb +19 -13
  9. data/lib/dm-sphinx-adapter/resource.rb +20 -13
  10. data/lib/dm-sphinx-adapter.rb +14 -11
  11. data/lib/riddle/client/filter.rb +53 -0
  12. data/lib/riddle/client/message.rb +65 -0
  13. data/lib/riddle/client/response.rb +84 -0
  14. data/lib/riddle/client.rb +619 -0
  15. data/lib/riddle.rb +28 -0
  16. data/test/files/model.rb +23 -0
  17. data/test/files/mysql5.sphinx.conf +97 -0
  18. data/test/files/mysql5.sql +26 -0
  19. data/test/helper.rb +51 -0
  20. data/test/test_adapter.rb +74 -28
  21. data/test/test_attribute.rb +36 -0
  22. data/test/test_index.rb +30 -0
  23. data/test/test_query.rb +47 -32
  24. data/test/test_resource.rb +17 -0
  25. metadata +18 -40
  26. data/lib/dm-sphinx-adapter/client.rb +0 -84
  27. data/lib/dm-sphinx-adapter/config.rb +0 -74
  28. data/lib/dm-sphinx-adapter/config_parser.rb +0 -67
  29. data/test/files/dm_sphinx_adapter_test.sql +0 -21
  30. data/test/files/resource_explicit.rb +0 -25
  31. data/test/files/resource_resource.rb +0 -19
  32. data/test/files/resource_searchable.rb +0 -16
  33. data/test/files/resource_storage_name.rb +0 -11
  34. data/test/files/resource_vanilla.rb +0 -7
  35. data/test/files/sphinx.conf +0 -78
  36. data/test/test_adapter_explicit.rb +0 -48
  37. data/test/test_adapter_resource.rb +0 -25
  38. data/test/test_adapter_searchable.rb +0 -23
  39. data/test/test_adapter_vanilla.rb +0 -46
  40. data/test/test_client.rb +0 -31
  41. data/test/test_config.rb +0 -75
  42. data/test/test_config_parser.rb +0 -29
  43. data/test/test_type_attribute.rb +0 -8
  44. data/test/test_type_index.rb +0 -8
@@ -2,21 +2,25 @@ module DataMapper
2
2
  module Adapters
3
3
  module Sphinx
4
4
 
5
- ##
6
5
  # Sphinx extended search query string from DataMapper query.
7
6
  class Query
8
7
  include Extlib::Assertions
9
8
 
10
- ##
11
- # Sphinx extended search query string from DataMapper query.
9
+ # Initialize a new extended Sphinx query from a DataMapper::Query object.
12
10
  #
13
11
  # If the query has no conditions an '' empty string will be generated possibly triggering Sphinx's full scan
14
12
  # mode.
15
13
  #
16
- # @see http://www.sphinxsearch.com/doc.html#extended-syntax
17
- # @see http://www.sphinxsearch.com/doc.html#searching
18
- # @see http://www.sphinxsearch.com/doc.html#conf-docinfo
19
- # @param [DataMapper::Query]
14
+ # ==== See
15
+ # * http://www.sphinxsearch.com/doc.html#searching
16
+ # * http://www.sphinxsearch.com/doc.html#conf-docinfo
17
+ # * http://www.sphinxsearch.com/doc.html#extended-syntax
18
+ #
19
+ # ==== Raises
20
+ # NotImplementedError:: DataMapper operators that can't be expressed in the extended sphinx query syntax.
21
+ #
22
+ # ==== Parameters
23
+ # query<DataMapper::Query>:: DataMapper query object.
20
24
  def initialize(query)
21
25
  assert_kind_of 'query', query, DataMapper::Query
22
26
  @query = []
@@ -38,18 +42,20 @@ module DataMapper
38
42
  end
39
43
  end
40
44
 
41
- ##
42
- # The extended sphinx query string.
43
- # @return [String]
45
+ # ==== Returns
46
+ # String:: The extended sphinx query string.
44
47
  def to_s
45
48
  @query.join(' ')
46
49
  end
47
50
 
48
51
  protected
49
- ##
50
52
  # Normalize and escape DataMapper query value(s) to escaped sphinx query values.
51
- # @param [String, Array] value The query value.
52
- # @return [Array]
53
+ #
54
+ # ==== Parameters
55
+ # value<String, Array>:: The query value.
56
+ #
57
+ # ==== Returns
58
+ # Array:: An array of one or more query values.
53
59
  def normalize_value(value)
54
60
  [value].flatten.map do |v|
55
61
  v.to_s.gsub(/[\(\)\|\-!@~"&\/]/){|char| "\\#{char}"}
@@ -2,8 +2,7 @@ module DataMapper
2
2
  module Adapters
3
3
  module Sphinx
4
4
 
5
- ##
6
- # Declare Sphinx indexes in your resource.
5
+ # Declare Sphinx indexes and attributes in your resource.
7
6
  #
8
7
  # model Items
9
8
  # include DataMapper::SphinxResource
@@ -35,15 +34,17 @@ module DataMapper
35
34
  model.instance_variable_set(:@sphinx_attributes, {})
36
35
  end
37
36
 
38
- ##
39
37
  # Defines a sphinx index on the resource.
40
38
  #
41
39
  # Indexes are naturally ordered, with delta indexes at the end of the list so that duplicate document IDs in
42
40
  # delta indexes override your main indexes.
43
41
  #
44
- # @param [Symbol] name the name of a sphinx index to search for this resource
45
- # @param [Hash(Symbol => String)] options a hash of available options
46
- # @see DataMapper::Adapters::Sphinx::Index
42
+ # ==== See
43
+ # * DataMapper::Adapters::Sphinx::Index
44
+ #
45
+ # ==== Parameters
46
+ # name<Symbol>:: The name of a sphinx index to search for this resource.
47
+ # options<Hash>:: A hash of available index options.
47
48
  def index(name, options = {})
48
49
  index = Index.new(self, name, options)
49
50
  indexes = sphinx_indexes(repository_name)
@@ -58,19 +59,23 @@ module DataMapper
58
59
  index
59
60
  end
60
61
 
61
- ##
62
62
  # List of declared sphinx indexes for this model.
63
+ #
64
+ # ==== Returns
65
+ # Array<DataMapper::Adapters::Sphinx::Index>
63
66
  def sphinx_indexes(repository_name = default_repository_name)
64
67
  @sphinx_indexes[repository_name] ||= []
65
68
  end
66
69
 
67
- ##
68
70
  # Defines a sphinx attribute on the resource.
69
71
  #
70
- # @param [Symbol] name the name of a sphinx attribute to order/restrict by for this resource
71
- # @param [Class] type the type to define this attribute as
72
- # @param [Hash(Symbol => String)] options a hash of available options
73
- # @see DataMapper::Adapters::Sphinx::Attribute
72
+ # ==== See
73
+ # DataMapper::Adapters::Sphinx::Attribute
74
+ #
75
+ # ==== Parameters
76
+ # name<Symbol>:: The name of a sphinx attribute to order/restrict by for this resource.
77
+ # type<Class>:: The type to define this attribute as.
78
+ # options<Hash>:: An optional hash of attribute options.
74
79
  def attribute(name, type, options = {})
75
80
  # Attributes are just properties without a getter/setter in the model.
76
81
  # This keeps DataMapper::Query happy when building queries.
@@ -79,8 +84,10 @@ module DataMapper
79
84
  attribute
80
85
  end
81
86
 
82
- ##
83
87
  # List of declared sphinx attributes for this model.
88
+ #
89
+ # ==== Returns
90
+ # Array<DataMapper::Adapters::Sphinx::Attribute>
84
91
  def sphinx_attributes(repository_name = default_repository_name)
85
92
  properties(repository_name).grep{|p| p.kind_of? Sphinx::Attribute}
86
93
  end
@@ -1,19 +1,22 @@
1
1
  require 'rubygems'
2
2
 
3
3
  # TODO: Hide the shitload of dm-core warnings or at least try to?
4
- $VERBOSE = nil
5
- gem 'dm-core', '~> 0.9.7'
6
- require 'dm-core'
4
+ old_verbose, $VERBOSE = $VERBOSE, nil
5
+ gem 'dm-core', '~> 0.9.7'
6
+ require 'dm-core'
7
+ $VERBOSE = old_verbose
7
8
 
8
- # TODO: I think I might move everything to DataMapper::Sphinx::* and ignore the default naming convention.
9
9
  require 'pathname'
10
- dir = Pathname(__FILE__).dirname.expand_path / 'dm-sphinx-adapter'
11
- require dir / 'config'
12
- require dir / 'config_parser'
13
- require dir / 'client'
14
- require dir / 'query'
10
+ lib = Pathname(__FILE__).dirname.expand_path
11
+ dir = lib / 'dm-sphinx-adapter'
12
+
13
+ # Bundled Riddle since the gem is very old and we don't need any of the config generation stuff.
14
+ $:.unshift lib
15
+ require 'riddle'
16
+
17
+ # TODO: Require farms suck. Do something about it.
15
18
  require dir / 'adapter'
16
- require dir / 'index'
17
19
  require dir / 'attribute'
20
+ require dir / 'index'
21
+ require dir / 'query'
18
22
  require dir / 'resource'
19
-
@@ -0,0 +1,53 @@
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|
37
+ case val
38
+ when TrueClass
39
+ 1.0
40
+ when FalseClass
41
+ 0.0
42
+ else
43
+ val.to_f
44
+ end
45
+ }
46
+ end
47
+ message.append_int self.exclude? ? 1 : 0
48
+
49
+ message.to_s
50
+ end
51
+ end
52
+ end
53
+ 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