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
@@ -1,74 +0,0 @@
1
- require 'rubygems'
2
-
3
- gem 'extlib', '~> 0.9.7'
4
- require 'extlib'
5
-
6
- module DataMapper
7
- module Adapters
8
- module Sphinx
9
- class Config
10
- include Extlib::Assertions
11
- attr_reader :config, :address, :log, :port
12
-
13
- ##
14
- # Read a sphinx configuration file.
15
- #
16
- # This class just gives you access to handy searchd {} configuration options.
17
- #
18
- # @see http://www.sphinxsearch.com/doc.html#confgroup-searchd
19
- def initialize(uri_or_options = {})
20
- assert_kind_of 'uri_or_options', uri_or_options, Addressable::URI, DataObjects::URI, Hash, String, Pathname
21
-
22
- options = normalize_options(uri_or_options)
23
- config = parse_config("#{options[:path]}") # Pathname#to_s is broken?
24
- @address = options[:host] || config['address'] || '0.0.0.0'
25
- @port = options[:port] || config['port'] || 3312
26
- @log = options[:log] || config['log'] || 'searchd.log'
27
- @pid_file = options[:pid_file] || config['pid_file']
28
- end
29
-
30
- ##
31
- # Indexer binary full path name and config argument.
32
- def indexer_bin(use_config = true)
33
- path = 'indexer' # TODO: Real.
34
- path << " --config #{config}" if config
35
- path
36
- end
37
-
38
- ##
39
- # Searchd binary full path name and config argument.
40
- def searchd_bin(use_config = true)
41
- path = 'searchd' # TODO: Real.
42
- path << " --config #{config}" if config
43
- path
44
- end
45
-
46
- def pid_file
47
- @pid_file or raise "Mandatory pid_file option missing from searchd configuration."
48
- end
49
-
50
- protected
51
- def normalize_options(uri_or_options)
52
- case uri_or_options
53
- when String, Addressable::URI then DataObjects::URI.parse(uri_or_options).attributes
54
- when DataObjects::URI then uri_or_options.attributes
55
- when Pathname then {:path => uri_or_options}
56
- else
57
- uri_or_options[:path] ||= uri_or_options.delete(:config) || uri_or_options.delete(:database)
58
- uri_or_options
59
- end
60
- end
61
-
62
- def parse_config(path)
63
- paths = []
64
- paths.push(path, path.gsub(%r{^/}, './'), path.gsub(%r{^\./}, '/')) unless path.blank?
65
- paths.push('/usr/local/etc/sphinx.conf', './sphinx.conf')
66
- paths.map!{|path| Pathname.new(path).expand_path}
67
-
68
- @config = paths.find{|path| path.readable? && `#{indexer_bin} --config #{path}` !~ /fatal|error/i}
69
- @config ? ConfigParser.parse(@config) : {}
70
- end
71
- end # Config
72
- end # Sphinx
73
- end # Adapters
74
- end # DataMapper
@@ -1,67 +0,0 @@
1
- require 'rubygems'
2
-
3
- gem 'extlib', '~> 0.9.7'
4
- require 'extlib'
5
-
6
- require 'pathname'
7
- require 'strscan'
8
-
9
- module DataMapper
10
- module Adapters
11
- module Sphinx
12
- module ConfigParser
13
- extend Extlib::Assertions
14
-
15
- ##
16
- # Parse a sphinx config file and return searchd options as a hash.
17
- #
18
- # @param [String] path Searches path, ./#{path}, /#{path}, /usr/local/etc/sphinx.conf, ./sphinx.conf in
19
- # that order.
20
- # @return [Hash]
21
- def self.parse(path)
22
- assert_kind_of 'path', path, Pathname, String
23
-
24
- config = Pathname(path).read
25
- config.gsub!(/\r\n|\r|\n/, "\n") # Everything in \n
26
- config.gsub!(/\s*\\\n\s*/, ' ') # Remove unixy line wraps.
27
-
28
- blocks(StringScanner.new(config), out = [])
29
- out.find{|c| c['type'] =~ /searchd/i} || {}
30
- end
31
-
32
- protected
33
- def self.blocks(conf, out = []) #:nodoc:
34
- if conf.scan(/\#[^\n]*\n/) || conf.scan(/\s+/)
35
- blocks(conf, out)
36
- elsif conf.scan(/indexer|searchd|source|index/i)
37
- out << group = {'type' => conf.matched}
38
- if conf.matched =~ /^(?:index|source)$/i
39
- conf.scan(/\s* ([\w_\-]+) (?:\s*:\s*([\w_\-]+))? \s*/x) or raise "Expected #{group[:type]} name."
40
- group['name'] = conf[1]
41
- group['ancestor'] = conf[2]
42
- end
43
- conf.scan(/\s*\{/) or raise %q{Expected '\{'.}
44
- pairs(conf, kv = {})
45
- group.merge!(kv)
46
- conf.scan(/\s*\}/) or raise %q{Expected '\}'.}
47
- blocks(conf, out)
48
- else
49
- raise "Unknown near: #{conf.peek(30)}" unless conf.eos?
50
- end
51
- end
52
-
53
- def self.pairs(conf, out = {}) #:nodoc:
54
- if conf.scan(/\#[^\n]*\n/) || conf.scan(/\s+/)
55
- pairs(conf, out)
56
- elsif conf.scan(/[\w_-]+/)
57
- key = conf.matched
58
- conf.scan(/\s*=/) or raise %q{Expected '='.}
59
- out[key] = conf.scan(/[^\n]*\n/).strip
60
- pairs(conf, out)
61
- end
62
- end
63
- end # ConfigParser
64
- end # Sphinx
65
- end # Adapters
66
- end # DataMapper
67
-
@@ -1,21 +0,0 @@
1
- drop table if exists delta;
2
- create table delta (
3
- name varchar(50) not null,
4
- updated_on datetime,
5
- primary key (name)
6
- ) engine=innodb default charset=utf8;
7
-
8
- drop table if exists items;
9
- create table items (
10
- id int(11) not null auto_increment,
11
- name varchar(50) not null,
12
- likes text not null,
13
- updated_on datetime,
14
- primary key (id),
15
- index (updated_on)
16
- ) engine=innodb default charset=utf8;
17
-
18
- insert into items (name, likes, updated_on) values
19
- ('one', 'I really like one!', now()),
20
- ('two', 'I really like two!', now()),
21
- ('three', 'I really like three!', now());
@@ -1,25 +0,0 @@
1
- require 'rubygems'
2
- require 'dm-is-searchable'
3
-
4
- class Explicit
5
- include DataMapper::Resource
6
- include DataMapper::SphinxResource
7
-
8
- property :id, Serial
9
- property :name, String
10
- property :likes, Text, :lazy => false
11
- property :updated_on, DateTime
12
-
13
- is :searchable
14
- repository(:search) do
15
- properties(:search).clear
16
- index :items
17
- index :items_delta, :delta => true
18
- property :name, String
19
- attribute :updated_on, DateTime
20
- end
21
-
22
- def self.default_storage_name
23
- 'item'
24
- end
25
- end # Explicit
@@ -1,19 +0,0 @@
1
- require 'rubygems'
2
- require 'dm-is-searchable'
3
-
4
- class Resource
5
- include DataMapper::Resource
6
- include DataMapper::SphinxResource
7
-
8
- property :id, Serial
9
- property :name, String
10
- property :likes, Text, :lazy => false
11
- property :updated_on, DateTime
12
-
13
- is :searchable
14
-
15
- def self.default_storage_name
16
- 'item'
17
- end
18
- end # Resource
19
-
@@ -1,16 +0,0 @@
1
- require 'rubygems'
2
- require 'dm-is-searchable'
3
-
4
- class Searchable
5
- include DataMapper::Resource
6
- property :id, Serial
7
- property :name, String
8
- property :likes, Text, :lazy => false
9
- property :updated_on, DateTime
10
-
11
- is :searchable
12
-
13
- def self.default_storage_name
14
- 'item'
15
- end
16
- end # Searchable
@@ -1,11 +0,0 @@
1
- class StorageName
2
- include DataMapper::Resource
3
- property :id, Serial
4
- property :name, String
5
- property :likes, Text, :lazy => false
6
- property :updated_on, DateTime
7
-
8
- def self.default_storage_name
9
- 'item'
10
- end
11
- end # StorageName
@@ -1,7 +0,0 @@
1
- class Vanilla
2
- include DataMapper::Resource
3
- property :id, Serial
4
- property :name, String
5
- property :likes, Text, :lazy => false
6
- property :updated_on, DateTime
7
- end # Vanilla
@@ -1,78 +0,0 @@
1
- # searchd and indexer must be run from the root directory of this lib.
2
-
3
- indexer
4
- {
5
- mem_limit = 64M
6
- }
7
-
8
- searchd
9
- {
10
- address = localhost
11
- port = 3312
12
- log = test/var/sphinx.log
13
- query_log = test/var/sphinx.query.log
14
- read_timeout = 5
15
- pid_file = test/var/sphinx.pid
16
- max_matches = 1000
17
- }
18
-
19
- source items
20
- {
21
- type = mysql
22
- sql_host = localhost
23
- sql_user = root
24
- sql_pass =
25
- sql_db = dm_sphinx_adapter_test
26
-
27
- sql_query_pre = set names utf8
28
- sql_query_pre = \
29
- replace into delta (name, updated_on) ( \
30
- select 'items', updated_on \
31
- from items \
32
- order by updated_on desc \
33
- limit 1\
34
- )
35
-
36
- sql_query = \
37
- select id, name, likes, unix_timestamp(updated_on) as updated_on \
38
- from items \
39
- where updated_on <= ( \
40
- select updated_on \
41
- from delta \
42
- where name = 'items'\
43
- )
44
-
45
- sql_query_info = select * from items where id = $id
46
- sql_attr_timestamp = updated_on
47
- }
48
-
49
- source items_delta : items
50
- {
51
- sql_query_pre = set names utf8
52
- sql_query = \
53
- select id, name, likes, unix_timestamp(updated_on) as updated_on \
54
- from items \
55
- where updated_on > ( \
56
- select updated_on \
57
- from delta \
58
- where name = 'items'\
59
- )
60
- }
61
-
62
- index items
63
- {
64
- source = items
65
- path = test/var/sphinx/items
66
- }
67
-
68
- index items_delta : items
69
- {
70
- source = items_delta
71
- path = test/var/sphinx/items_delta
72
- }
73
-
74
- index vanillas
75
- {
76
- type = distributed
77
- local = items
78
- }
@@ -1,48 +0,0 @@
1
- require 'test_adapter'
2
- require 'files/resource_explicit'
3
-
4
- class TestAdapterExplicit < TestAdapter
5
- def setup
6
- super
7
- DataMapper.setup(:search, :adapter => 'sphinx', :config => @config, :managed => true)
8
- end
9
-
10
- def teardown
11
- DataMapper.repository(:search).adapter.client.stop
12
- super
13
- end
14
-
15
- def test_initialize
16
- assert_nothing_raised{ Explicit.new }
17
- end
18
-
19
- def test_search_properties
20
- assert_equal Explicit.all, Explicit.search
21
- assert_equal [Explicit.first(:id => 2)], Explicit.search(:name => 'two')
22
- end
23
-
24
- def test_search_delta
25
- resource = Explicit.create(:name => 'four', :likes => 'chicken', :updated_on => Time.now)
26
- DataMapper.repository(:search).adapter.client.index('items_delta')
27
- assert_equal [resource], Explicit.search(:name => 'four')
28
- end
29
-
30
- def test_search_attribute_timestamp
31
- time = Time.now
32
- resource = Explicit.create(:name => 'four', :likes => 'chicken', :updated_on => time)
33
- DataMapper.repository(:search).adapter.client.index('items_delta')
34
-
35
- assert_equal [resource], Explicit.search(:updated_on => time.to_i)
36
- assert_equal [resource], Explicit.search(:updated_on => (time .. time + 1))
37
- assert_equal [], Explicit.search(:updated_on => (time - 60 * 60))
38
- assert_equal [], Explicit.search(:updated_on => (time + 60 * 60))
39
- end
40
-
41
- def test_search_attribute_boolean
42
- # TODO:
43
- end
44
-
45
- def test_search_attribute_integer
46
- # TODO
47
- end
48
- end # TestAdapterExplicit
@@ -1,25 +0,0 @@
1
- require 'test_adapter'
2
- require 'files/resource_resource'
3
-
4
- class TestAdapterResource < TestAdapter
5
- def setup
6
- super
7
- DataMapper.setup(:search, :adapter => 'sphinx', :config => @config, :managed => true)
8
- end
9
-
10
- def teardown
11
- DataMapper.repository(:search).adapter.client.stop
12
- super
13
- end
14
-
15
- def test_initialize
16
- assert_nothing_raised{ Resource.new }
17
- end
18
-
19
- def test_search_properties
20
- assert_equal Resource.all, Resource.search
21
- assert_equal [Resource.first(:id => 2)], Resource.search(:name => 'two')
22
- assert_equal [Resource.first(:id => 2)], Resource.search(:conditions => ['two'])
23
- end
24
- end # TestAdapterResource
25
-
@@ -1,23 +0,0 @@
1
- require 'test_adapter'
2
- require 'files/resource_searchable'
3
-
4
- class TestAdapterSearchable < TestAdapter
5
- def setup
6
- super
7
- DataMapper.setup(:search, :adapter => 'sphinx', :config => @config, :managed => true)
8
- end
9
-
10
- def teardown
11
- DataMapper.repository(:search).adapter.client.stop
12
- super
13
- end
14
-
15
- def test_initialize
16
- assert_nothing_raised{ Searchable.new }
17
- end
18
-
19
- def test_search
20
- assert_equal Searchable.all, Searchable.search
21
- assert_equal [Searchable.first(:id => 2)], Searchable.search(:name => 'two')
22
- end
23
- end # TestAdapterSearchable
@@ -1,46 +0,0 @@
1
- require 'test_adapter'
2
- require 'files/resource_vanilla'
3
- require 'files/resource_storage_name'
4
-
5
- class TestAdapterVanilla < TestAdapter
6
- def setup
7
- super
8
- DataMapper.setup(:default, :adapter => 'sphinx', :config => @config, :managed => true)
9
- end
10
-
11
- def teardown
12
- DataMapper.repository(:default).adapter.client.stop
13
- super
14
- end
15
-
16
- def test_initialize
17
- assert_nothing_raised{ Vanilla.new }
18
- assert_nothing_raised{ StorageName.new }
19
- end
20
-
21
- def test_all
22
- assert_equal [{:id => 1}, {:id => 2}, {:id => 3}], Vanilla.all
23
- assert_equal [{:id => 1}], Vanilla.all(:name => 'one')
24
- end
25
-
26
- def test_all_limit
27
- assert_equal [{:id => 1}], Vanilla.all(:limit => 1)
28
- assert_equal [{:id => 1}, {:id => 2}], Vanilla.all(:limit => 2)
29
- end
30
-
31
- def test_all_offset
32
- assert_equal [{:id => 1}, {:id => 2}, {:id => 3}], Vanilla.all(:offset => 0)
33
- assert_equal [{:id => 2}, {:id => 3}], Vanilla.all(:offset => 1)
34
- assert_equal [], Vanilla.all(:offset => 3)
35
- end
36
-
37
- def test_first
38
- assert_equal({:id => 1}, Vanilla.first(:name => 'one'))
39
- assert_nil Vanilla.first(:name => 'missing')
40
- end
41
-
42
- def test_storage_name
43
- assert_equal Vanilla.all, StorageName.all
44
- assert_equal Vanilla.first, StorageName.first
45
- end
46
- end
data/test/test_client.rb DELETED
@@ -1,31 +0,0 @@
1
- require 'test_adapter'
2
-
3
- class TestClient < TestAdapter
4
- def test_initialize
5
- assert_nothing_raised { DataMapper::Adapters::Sphinx::Client.new(@config) }
6
- end
7
-
8
- def test_index
9
- client = DataMapper::Adapters::Sphinx::Client.new(@config)
10
- assert_nothing_raised{ client.index }
11
- assert_nothing_raised{ client.index 'items' }
12
- assert_nothing_raised{ client.index '*' }
13
- assert_nothing_raised{ client.index ['items', 'items_delta'] }
14
- end
15
-
16
- def test_managed_initialize
17
- assert_nothing_raised { DataMapper::Adapters::Sphinx::ManagedClient.new(@config) }
18
- end
19
-
20
- def test_search
21
- begin
22
- client = DataMapper::Adapters::Sphinx::ManagedClient.new(@config)
23
- client.index
24
- assert match = client.search('two')
25
- assert_equal 1, match[:total]
26
- assert_equal 2, match[:matches][0][:doc]
27
- ensure
28
- client.stop
29
- end
30
- end
31
- end # TestClient
data/test/test_config.rb DELETED
@@ -1,75 +0,0 @@
1
- require 'test/unit'
2
- require 'dm-sphinx-adapter'
3
-
4
- class TestConfig < Test::Unit::TestCase
5
- def setup
6
- base = Pathname(__FILE__).dirname.expand_path
7
- @config = base / 'files' / 'sphinx.conf'
8
- @log = base / 'var' / 'sphinx.log'
9
- end
10
-
11
- def test_initialize
12
- assert_nothing_raised{ config }
13
- assert_nothing_raised{ config({}) }
14
- assert_nothing_raised{ config(:config => nil) }
15
- assert_nothing_raised{ config(:config => 'blah') }
16
- assert_nothing_raised{ config(:config => @log) }
17
- end
18
-
19
- def test_initalize_defaults
20
- assert c = config({})
21
- assert_equal '0.0.0.0', c.address
22
- assert_equal 3312, c.port
23
- assert_equal 'searchd.log', c.log
24
- assert_raise(RuntimeError){ c.pid_file }
25
- assert_nil c.config
26
- end
27
-
28
- def test_initalize_options_hash
29
- assert c = config(
30
- :host => 'options',
31
- :port => 1234,
32
- :log => 'log.log',
33
- :pid_file => 'pid.pid'
34
- )
35
-
36
- assert_equal 'options', c.address
37
- assert_equal 1234, c.port
38
- assert_equal 'log.log', c.log
39
- assert_equal 'pid.pid', c.pid_file
40
- assert_nil c.config
41
- end
42
-
43
- def test_initialize_options_string
44
- assert c = config("sphinx://options:1234")
45
- assert_equal 'options', c.address
46
- assert_equal 1234, c.port
47
- assert_equal 'searchd.log', c.log
48
- assert_raise(RuntimeError){ c.pid_file }
49
- assert_nil c.config
50
- end
51
-
52
- def test_initialize_config
53
- assert c = config(:config => @config)
54
- assert_equal 'localhost', c.address
55
- assert_equal '3312', c.port
56
- assert_equal 'test/var/sphinx.log', c.log
57
- assert_equal 'test/var/sphinx.pid', c.pid_file
58
- assert_equal @config, c.config
59
- end
60
-
61
- def test_initialize_database_hash
62
- assert c = config(:database => @config)
63
- assert_equal @config, c.config
64
- end
65
-
66
- def test_initialize_database_string
67
- assert c = config("sphinx://localhost/test/files/sphinx.conf")
68
- assert_equal @config, c.config
69
- end
70
-
71
- protected
72
- def config(options = {:config => @config})
73
- DataMapper::Adapters::Sphinx::Config.new(options)
74
- end
75
- end # TestConfig
@@ -1,29 +0,0 @@
1
- require 'test/unit'
2
- require 'dm-sphinx-adapter'
3
-
4
- class TestConfigParser < Test::Unit::TestCase
5
- def setup
6
- base = Pathname(__FILE__).dirname.expand_path
7
- @config = base / 'files' / 'sphinx.conf'
8
- @log = base / 'var' / 'sphinx.log'
9
- end
10
-
11
- def test_parse
12
- assert_nothing_raised{ parse }
13
- assert_raise(Errno::ENOENT){ parse('blah') }
14
- assert_raise(RuntimeError){ parse(@log) }
15
- end
16
-
17
- def test_searchd
18
- assert_kind_of Hash, searchd = parse
19
- assert_equal 'localhost', searchd['address']
20
- assert_equal '3312', searchd['port']
21
- assert_equal 'test/var/sphinx.pid', searchd['pid_file']
22
- assert_equal 'test/var/sphinx.log', searchd['log']
23
- end
24
-
25
- protected
26
- def parse(config = @config)
27
- DataMapper::Adapters::Sphinx::ConfigParser.parse(config)
28
- end
29
- end # TestConfigParser
@@ -1,8 +0,0 @@
1
- require 'dm-sphinx-adapter'
2
-
3
- class TestTypeAttribute < Test::Unit::TestCase
4
- def test_initialize
5
- # TODO:
6
- assert true
7
- end
8
- end
@@ -1,8 +0,0 @@
1
- require 'dm-sphinx-adapter'
2
-
3
- class TestTypeIndex < Test::Unit::TestCase
4
- def test_initialize
5
- # TODO:
6
- assert true
7
- end
8
- end