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