shanna-dm-sphinx-adapter 0.4
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.
- data/History.txt +17 -0
- data/LICENCE.txt +20 -0
- data/Manifest.txt +31 -0
- data/README.txt +195 -0
- data/Rakefile +23 -0
- data/dm-sphinx-adapter.gemspec +41 -0
- data/lib/dm-sphinx-adapter/adapter.rb +218 -0
- data/lib/dm-sphinx-adapter/attribute.rb +39 -0
- data/lib/dm-sphinx-adapter/client.rb +86 -0
- data/lib/dm-sphinx-adapter/config.rb +74 -0
- data/lib/dm-sphinx-adapter/config_parser.rb +67 -0
- data/lib/dm-sphinx-adapter/index.rb +25 -0
- data/lib/dm-sphinx-adapter/resource.rb +96 -0
- data/lib/dm-sphinx-adapter.rb +18 -0
- data/test/files/dm_sphinx_adapter_test.sql +21 -0
- data/test/files/resource_explicit.rb +25 -0
- data/test/files/resource_resource.rb +19 -0
- data/test/files/resource_searchable.rb +16 -0
- data/test/files/resource_storage_name.rb +11 -0
- data/test/files/resource_vanilla.rb +7 -0
- data/test/files/sphinx.conf +78 -0
- data/test/test_adapter.rb +38 -0
- data/test/test_adapter_explicit.rb +48 -0
- data/test/test_adapter_resource.rb +25 -0
- data/test/test_adapter_searchable.rb +23 -0
- data/test/test_adapter_vanilla.rb +46 -0
- data/test/test_client.rb +31 -0
- data/test/test_config.rb +75 -0
- data/test/test_config_parser.rb +29 -0
- data/test/test_type_attribute.rb +8 -0
- data/test/test_type_index.rb +8 -0
- metadata +123 -0
@@ -0,0 +1,74 @@
|
|
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
|
@@ -0,0 +1,67 @@
|
|
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
|
+
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module DataMapper
|
2
|
+
module Adapters
|
3
|
+
module Sphinx
|
4
|
+
class Index
|
5
|
+
include Assertions
|
6
|
+
|
7
|
+
attr_reader :model, :name, :options
|
8
|
+
|
9
|
+
def initialize(model, name, options = {})
|
10
|
+
assert_kind_of 'model', model, Model
|
11
|
+
assert_kind_of 'name', name, Symbol, String
|
12
|
+
assert_kind_of 'options', options, Hash
|
13
|
+
|
14
|
+
@model = model
|
15
|
+
@name = name.to_sym
|
16
|
+
@delta = options.fetch(:delta, nil)
|
17
|
+
end
|
18
|
+
|
19
|
+
def delta?
|
20
|
+
!!@delta
|
21
|
+
end
|
22
|
+
end # Index
|
23
|
+
end # Sphinx
|
24
|
+
end # Adapters
|
25
|
+
end # DataMapper
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module DataMapper
|
2
|
+
module Adapters
|
3
|
+
module Sphinx
|
4
|
+
|
5
|
+
##
|
6
|
+
# Declare Sphinx indexes in your resource.
|
7
|
+
#
|
8
|
+
# model Items
|
9
|
+
# include DataMapper::SphinxResource
|
10
|
+
#
|
11
|
+
# # .. normal properties and such for :default
|
12
|
+
#
|
13
|
+
# repository(:search) do
|
14
|
+
# # Query some_index, some_index_delta in that order.
|
15
|
+
# index :some_index
|
16
|
+
# index :some_index_delta, :delta => true
|
17
|
+
#
|
18
|
+
# # Sortable by some attributes.
|
19
|
+
# attribute :updated_at, DateTime # sql_attr_timestamp
|
20
|
+
# attribute :age, Integer # sql_attr_uint
|
21
|
+
# attribute :deleted, Boolean # sql_attr_bool
|
22
|
+
# end
|
23
|
+
# end
|
24
|
+
module Resource
|
25
|
+
def self.included(model) #:nodoc:
|
26
|
+
model.class_eval do
|
27
|
+
include DataMapper::Resource
|
28
|
+
extend ClassMethods
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
module ClassMethods
|
33
|
+
def self.extended(model) #:nodoc:
|
34
|
+
model.instance_variable_set(:@sphinx_indexes, {})
|
35
|
+
model.instance_variable_set(:@sphinx_attributes, {})
|
36
|
+
end
|
37
|
+
|
38
|
+
##
|
39
|
+
# Defines a sphinx index on the resource.
|
40
|
+
#
|
41
|
+
# Indexes are naturally ordered, with delta indexes at the end of the list so that duplicate document IDs in
|
42
|
+
# delta indexes override your main indexes.
|
43
|
+
#
|
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
|
47
|
+
def index(name, options = {})
|
48
|
+
index = Index.new(self, name, options)
|
49
|
+
indexes = sphinx_indexes(repository_name)
|
50
|
+
indexes << index
|
51
|
+
|
52
|
+
# TODO: I'm such a Ruby nub. In the meantime I've gone back to my Perl roots.
|
53
|
+
# This is a Schwartzian transform to sort delta indexes to the bottom and natural sort by name.
|
54
|
+
mapped = indexes.map{|i| [(i.delta? ? 1 : 0), i.name, i]}
|
55
|
+
sorted = mapped.sort{|a, b| a[0] <=> b[0] || a[1] <=> b[1]}
|
56
|
+
indexes.replace(sorted.map{|i| i[2]})
|
57
|
+
|
58
|
+
index
|
59
|
+
end
|
60
|
+
|
61
|
+
##
|
62
|
+
# List of declared sphinx indexes for this model.
|
63
|
+
def sphinx_indexes(repository_name = default_repository_name)
|
64
|
+
@sphinx_indexes[repository_name] ||= []
|
65
|
+
end
|
66
|
+
|
67
|
+
##
|
68
|
+
# Defines a sphinx attribute on the resource.
|
69
|
+
#
|
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
|
74
|
+
def attribute(name, type, options = {})
|
75
|
+
# Attributes are just properties without a getter/setter in the model.
|
76
|
+
# This keeps DataMapper::Query happy when building queries.
|
77
|
+
attribute = Sphinx::Attribute.new(self, name, type, options)
|
78
|
+
properties(repository_name)[attribute.name] = attribute
|
79
|
+
attribute
|
80
|
+
end
|
81
|
+
|
82
|
+
##
|
83
|
+
# List of declared sphinx attributes for this model.
|
84
|
+
def sphinx_attributes(repository_name = default_repository_name)
|
85
|
+
properties(repository_name).grep{|p| p.kind_of? Sphinx::Attribute}
|
86
|
+
end
|
87
|
+
|
88
|
+
end # ClassMethods
|
89
|
+
end # Resource
|
90
|
+
end # Sphinx
|
91
|
+
end # Adapters
|
92
|
+
|
93
|
+
# Follow DM naming convention.
|
94
|
+
SphinxResource = Adapters::Sphinx::Resource
|
95
|
+
end # DataMapper
|
96
|
+
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
|
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'
|
7
|
+
|
8
|
+
# TODO: I think I might move everything to DataMapper::Sphinx::* and ignore the default naming convention.
|
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 / 'adapter'
|
15
|
+
require dir / 'index'
|
16
|
+
require dir / 'attribute'
|
17
|
+
require dir / 'resource'
|
18
|
+
|
@@ -0,0 +1,21 @@
|
|
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());
|
@@ -0,0 +1,25 @@
|
|
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
|
@@ -0,0 +1,19 @@
|
|
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
|
+
|
@@ -0,0 +1,16 @@
|
|
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
|
@@ -0,0 +1,78 @@
|
|
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
|
+
}
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'dm-sphinx-adapter'
|
2
|
+
require 'test/unit'
|
3
|
+
|
4
|
+
# DataMapper::Logger.new(STDOUT, :debug)
|
5
|
+
|
6
|
+
class TestAdapter < Test::Unit::TestCase
|
7
|
+
def setup
|
8
|
+
# TODO: A little too brutal even by my standards.
|
9
|
+
Dir.chdir(File.join(File.dirname(__FILE__), 'files')) do
|
10
|
+
system 'mysql -u root dm_sphinx_adapter_test < dm_sphinx_adapter_test.sql' \
|
11
|
+
or raise %q{Tests require the dm_sphinx_adapter_test database.}
|
12
|
+
end
|
13
|
+
|
14
|
+
DataMapper.setup(:default, 'mysql://localhost/dm_sphinx_adapter_test')
|
15
|
+
|
16
|
+
@config = Pathname.new(__FILE__).dirname.expand_path / 'files' / 'sphinx.conf'
|
17
|
+
@client = DataMapper::Adapters::Sphinx::ManagedClient.new(:config => @config)
|
18
|
+
@client.index
|
19
|
+
sleep 1
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_unmanaged_setup
|
23
|
+
assert DataMapper.setup(:sphinx, :adapter => 'sphinx')
|
24
|
+
assert_kind_of DataMapper::Adapters::SphinxAdapter, repository(:sphinx).adapter
|
25
|
+
assert_kind_of DataMapper::Adapters::Sphinx::Client, repository(:sphinx).adapter.client
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_managed_setup
|
29
|
+
assert DataMapper.setup(:sphinx, :adapter => 'sphinx', :config => @config, :managed => true)
|
30
|
+
assert_kind_of DataMapper::Adapters::SphinxAdapter, repository(:sphinx).adapter
|
31
|
+
assert_kind_of DataMapper::Adapters::Sphinx::ManagedClient, repository(:sphinx).adapter.client
|
32
|
+
end
|
33
|
+
|
34
|
+
def teardown
|
35
|
+
@client.stop
|
36
|
+
sleep 1
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,48 @@
|
|
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
|
@@ -0,0 +1,25 @@
|
|
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
|
+
|
@@ -0,0 +1,23 @@
|
|
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
|
@@ -0,0 +1,46 @@
|
|
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
ADDED
@@ -0,0 +1,31 @@
|
|
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
|